Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strong-walls-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gram-ai/elements": minor
---

Migrate elements to new repo (should be noop)
5 changes: 4 additions & 1 deletion .github/filters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ cli:
- 'server/gen/**'

tsframework:
- 'ts-framework/**'
- 'ts-framework/**'

elements:
- 'elements/**'
59 changes: 59 additions & 0 deletions .github/workflows/elements-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Generate Elements TypeDoc Documentation

on:
push:
branches:
- main
paths:
- "elements/**"
workflow_dispatch: # Allow manual triggering

permissions:
contents: write

jobs:
generate-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
with:
fetch-depth: 0

- name: Setup Mise
uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3.5.1
with:
install: true
cache: true
env: false

- name: Prepare GitHub Actions environment
run: mise run github

- name: Cache PNPM
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Generate TypeDoc
working-directory: elements
run: pnpm run docs

- name: Commit and push if changed
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add elements/docs/
git diff --quiet && git diff --staged --quiet || (git commit -m "docs: update TypeDoc API documentation [skip ci]" && git push)

- name: Prune PNPM store
if: success()
run: pnpm store prune
66 changes: 66 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
cli: ${{ steps.gates.outputs.cli }}
functions: ${{ steps.gates.outputs.functions }}
tsframework: ${{ steps.gates.outputs.tsframework }}
elements: ${{ steps.gates.outputs.elements }}
steps:
- name: Checkout source code
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
Expand Down Expand Up @@ -74,6 +75,13 @@ jobs:
echo "TypeScript framework jobs will be skipped."
fi

if [[ "${{ steps.filter.outputs.elements }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "elements=true" >> $GITHUB_OUTPUT
echo "Elements jobs will run."
else
echo "Elements jobs will be skipped."
fi

docker-build-server:
runs-on: ubicloud-standard-4
needs: [changes, server-build-lint]
Expand Down Expand Up @@ -867,3 +875,61 @@ jobs:
- name: Prune PNPM store
if: ${{ needs.changes.outputs.tsframework == 'true' && success() }}
run: pnpm store prune

elements-build-lint:
runs-on: ubicloud-standard-4
needs: changes
steps:
- name: Skip if no elements changes exist
if: ${{ needs.changes.outputs.elements != 'true' }}
run: echo "No elements changes detected — skipping elements-build-lint."

- name: Checkout
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0

- name: Setup Mise
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3.5.1
with:
install: true
cache: true
env: false

- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.elements == 'true' }}
run: mise run github

- name: Cache PNPM
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}

- name: Install dependencies
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm install --frozen-lockfile

- name: Build
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm build
working-directory: elements

- name: Lint
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm lint
working-directory: elements

- name: Type check
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm type-check
working-directory: elements

- name: Prune PNPM store
if: ${{ needs.changes.outputs.elements == 'true' && success() }}
run: pnpm store prune
1 change: 1 addition & 0 deletions elements/.env.local.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GRAM_API_KEY=xxx
18 changes: 18 additions & 0 deletions elements/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
dist
node_modules
*storybook.log
storybook-static
*.env
.env.*

# Allow the local environment template file
!.env.local.template

# mise local overrides
.mise.local.toml

# Ignore tgz files
*.tgz

# Ignore .DS_Store
.DS_Store
1 change: 1 addition & 0 deletions elements/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--auto-install-peers=true
7 changes: 7 additions & 0 deletions elements/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"plugins": ["prettier-plugin-tailwindcss"]
}
65 changes: 65 additions & 0 deletions elements/.storybook/GlobalDecorator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useMemo } from 'react'
import { ElementsProvider } from '../src/contexts/ElementsProvider'
import { ElementsConfig } from '../src/types'
import merge from 'lodash.merge'
import { recommended } from '../src/plugins'

interface ElementsDecoratorProps {
children: React.ReactNode
// Partial so stories can override only what they need
config?: Partial<ElementsConfig>
}

const DEFAULT_ELEMENTS_CONFIG: ElementsConfig = {
projectSlug: 'adamtest',
mcp: 'https://chat.speakeasy.com/mcp/speakeasy-team-my_api',
variant: 'widget',
welcome: {
title: 'Hello there!',
subtitle: 'How can I help you today?',
suggestions: [
{
title: 'Discover available tools',
label: 'Find out what tools are available',
action: 'Call all tools available',
},
],
},
composer: {
placeholder: 'Ask me anything...',
attachments: true,
},
modal: {
defaultOpen: true,
expandable: true,
defaultExpanded: true,
title: 'Gram Elements Demo',
},
tools: {
expandToolGroupsByDefault: true,
},
plugins: recommended,
}

/**
* Global decorator that wraps all stories in the AssistantRuntimeProvider,
* which provides the chat runtime to the story.
* Note: This assumes that all stories require a chat runtime, but we move back to
* per story decorator in the future.
* @param children - The children to render.
* @returns
*/
export const ElementsDecorator: React.FC<ElementsDecoratorProps> = ({
children,
config,
}) => {
const finalConfig = useMemo(
() => merge({}, DEFAULT_ELEMENTS_CONFIG, config ?? {}),
[config]
)
return (
<ElementsProvider config={finalConfig}>
<div className="h-screen bg-zinc-50">{children}</div>
</ElementsProvider>
)
}
15 changes: 15 additions & 0 deletions elements/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { StorybookConfig } from '@storybook/react-vite'

const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-docs'],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: './.storybook/vite.config.mts',
},
},
},
}
export default config
28 changes: 28 additions & 0 deletions elements/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Preview } from '@storybook/react-vite'
import { ElementsDecorator } from './GlobalDecorator'
import React from 'react'
import '../src/global.css'

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
decorators: [
(Story, context) => {
// Stories can override config via parameters.elements
const elementsParams = context.parameters.elements ?? {}
return (
<ElementsDecorator config={elementsParams.config}>
<Story />
</ElementsDecorator>
)
},
],
}

export default preview
25 changes: 25 additions & 0 deletions elements/.storybook/vite.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { defineConfig, ViteDevServer } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createElementsServerHandlers } from '../src/server'

const __dirname = dirname(fileURLToPath(import.meta.url))

const apiMiddlewarePlugin = () => ({
name: 'chat-api-middleware',
configureServer(server: ViteDevServer) {
const handlers = createElementsServerHandlers()
server.middlewares.use('/chat/completions', handlers.chat)
},
})

export default defineConfig({
plugins: [react(), tailwindcss(), apiMiddlewarePlugin()],
resolve: {
alias: {
'@': resolve(__dirname, '../src'),
},
},
})
27 changes: 27 additions & 0 deletions elements/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Contributing to `@gram-ai/elements`

## Setup

Ensure that you have your Gram API key setup in your `.env.local` file (rename the template).

Then simply run the Storybook which has a preconfigured dev middleware for chat completions so that you can test the components against a real LLM:

```bash
pnpm storybook
```

## Third party dependencies

If adding a heavy dependency, please make sure you mark it as a peer in `peerDependencies`, mark it as optional in `peerDependenciesMeta` (so the server package doesn't require it), and mark it as an `external` in the vite configuration.

## Documentation

We use TypeDoc to automatically generate markdown documentation via a github action - the documentation is generated from the TypeScript types. However you can generate documentation locally by following the guide below:

```bash
# Generate documentation
pnpm run docs

# Watch mode (regenerate on changes)
pnpm run docs:watch
```
Loading
Loading