diff --git a/packages/jumble/src/iframe-ctx.ts b/packages/jumble/src/iframe-ctx.ts index 1e8700564..1ea2dff73 100644 --- a/packages/jumble/src/iframe-ctx.ts +++ b/packages/jumble/src/iframe-ctx.ts @@ -3,7 +3,7 @@ import { IPC, setIframeContextHandler, } from "@commontools/iframe-sandbox"; -import { components } from "@commontools/ui"; +import { v1 } from "@commontools/ui"; import { Action, addCommonIDfromObjectID, @@ -20,7 +20,7 @@ import { updateJob, } from "@/contexts/ActivityContext.tsx"; -const CommonCharmElement = components.CommonCharm.CommonCharmElement; +const CommonCharmElement = v1.components.CommonCharm.CommonCharmElement; const llm = new LLMClient(); // FIXME(ja): perhaps this could be in common-charm? needed to enable iframe with sandboxing diff --git a/packages/ui/.claude/settings.local.json b/packages/ui/.claude/settings.local.json new file mode 100644 index 000000000..460fce581 --- /dev/null +++ b/packages/ui/.claude/settings.local.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Bash(deno check:*)", + "Bash(deno lint:*)", + "Bash(deno fmt:*)", + "Bash(find:*)", + "Bash(ls:*)", + "Bash(grep:*)" + ], + "deny": [] + } +} diff --git a/packages/ui/LLM-COMPONENT-INSTRUCTIONS.md b/packages/ui/LLM-COMPONENT-INSTRUCTIONS.md new file mode 100644 index 000000000..d415f1d2b --- /dev/null +++ b/packages/ui/LLM-COMPONENT-INSTRUCTIONS.md @@ -0,0 +1,681 @@ +# LLM Component Composition Guide + +This document provides comprehensive component specifications for Language +Models to assist with web component composition using the Common CT library. + +## Component Library Overview + +The Common CT library provides 39 secure web components that follow the +shadcn/ui design system. All components: + +- Use custom element tags prefixed with `ct-` +- Support Shadow DOM encapsulation +- Emit custom events prefixed with `ct-` +- Follow strict security constraints (no external resources, limited events) + +## Component Reference + +### 1. ct-button + +**Purpose**: Interactive button element **Tag**: `` **Attributes**: + +- `variant` - "default" | "destructive" | "outline" | "secondary" | "ghost" | + "link" +- `size` - "default" | "sm" | "lg" | "icon" +- `disabled` - boolean +- `type` - "button" | "submit" | "reset" **Events**: +- `ct-click` - Fired on click with detail: `{ variant, size }` **Slots**: + Default slot for button content **Example**: + +```html +Click Me +``` + +### 2. ct-input + +**Purpose**: Text input field **Tag**: `` **Attributes**: + +- `type` - "text" | "email" | "password" | "number" | "search" | "tel" | "url" | + "date" | "time" | "datetime-local" +- `placeholder` - string +- `value` - string +- `disabled` - boolean +- `readonly` - boolean +- `required` - boolean +- `name` - string +- `min` - string/number +- `max` - string/number +- `step` - string/number +- `pattern` - string +- `autocomplete` - string **Events**: +- `ct-input` - Fired on input with detail: `{ value, name }` +- `ct-change` - Fired on change with detail: `{ value, name }` +- `ct-focus` - Fired on focus +- `ct-blur` - Fired on blur **Example**: + +```html + +``` + +### 3. ct-textarea + +**Purpose**: Multi-line text input **Tag**: `` **Attributes**: + +- `placeholder` - string +- `value` - string +- `disabled` - boolean +- `readonly` - boolean +- `required` - boolean +- `name` - string +- `rows` - number +- `cols` - number +- `maxlength` - number +- `auto-resize` - boolean **Events**: +- `ct-input` - Fired on input with detail: `{ value, name }` +- `ct-change` - Fired on change with detail: `{ value, name }` **Example**: + +```html + +``` + +### 4. ct-checkbox + +**Purpose**: Binary selection input **Tag**: `` **Attributes**: + +- `checked` - boolean +- `disabled` - boolean +- `name` - string +- `value` - string +- `required` - boolean +- `indeterminate` - boolean **Events**: +- `ct-change` - Fired on change with detail: `{ checked, indeterminate }` + +**Example**: + +```html +Accept terms +``` + +### 5. ct-radio + +**Purpose**: Single selection from group **Tag**: `` **Attributes**: + +- `checked` - boolean +- `disabled` - boolean +- `name` - string (required for grouping) +- `value` - string (required) +- `required` - boolean **Events**: +- `ct-change` - Fired on change with detail: `{ value, checked }` **Note**: Must + be used within `ct-radio-group` for proper functionality **Example**: + +```html + + Red + Blue + +``` + +### 6. ct-radio-group + +**Purpose**: Container for radio buttons **Tag**: `` +**Attributes**: + +- `name` - string (required) +- `value` - string (currently selected value) +- `disabled` - boolean **Events**: +- `ct-change` - Fired when selection changes with detail: `{ value }` **Slots**: + Default slot for ct-radio elements + +### 7. ct-switch + +**Purpose**: Toggle switch **Tag**: `` **Attributes**: + +- `checked` - boolean +- `disabled` - boolean +- `name` - string **Events**: +- `ct-change` - Fired on toggle with detail: `{ checked }` **Example**: + +```html +Enable notifications +``` + +### 8. ct-slider + +**Purpose**: Range input slider **Tag**: `` **Attributes**: + +- `value` - number +- `min` - number (default: 0) +- `max` - number (default: 100) +- `step` - number (default: 1) +- `disabled` - boolean +- `name` - string **Events**: +- `ct-change` - Fired on value change with detail: `{ value }` **Example**: + +```html + +``` + +### 9. ct-toggle + +**Purpose**: Toggle button **Tag**: `` **Attributes**: + +- `pressed` - boolean +- `disabled` - boolean +- `variant` - "default" | "outline" +- `size` - "default" | "sm" | "lg" +- `value` - string (for toggle groups) **Events**: +- `ct-change` - Fired on toggle with detail: `{ pressed }` **Slots**: Default + slot for content **Example**: + +```html +Bold +``` + +### 10. ct-toggle-group + +**Purpose**: Group of toggle buttons **Tag**: `` +**Attributes**: + +- `type` - "single" | "multiple" +- `value` - string (for single) | string[] (for multiple) +- `disabled` - boolean **Events**: +- `ct-change` - Fired on selection change with detail: `{ value }` **Slots**: + Default slot for ct-toggle elements + +### 11. ct-label + +**Purpose**: Form field label **Tag**: `` **Attributes**: + +- `for` - string (ID of associated input) +- `required` - boolean (shows asterisk) +- `disabled` - boolean **Events**: +- `ct-label-click` - Fired on click with detail: `{ targetId, targetElement }` + +**Slots**: Default slot for label text **Example**: + +```html +Email Address + +``` + +### 12. ct-card + +**Purpose**: Content container **Tag**: `` **Attributes**: None +**Events**: None **Slots**: + +- `header` - Card header content +- `content` - Main card content +- `footer` - Card footer content **Example**: + +```html + +

Card Title

+

Card content goes here

+ Action +
+``` + +### 13. ct-badge + +**Purpose**: Status indicator or label **Tag**: `` **Attributes**: + +- `variant` - "default" | "secondary" | "destructive" | "outline" +- `removable` - boolean (shows X button) **Events**: +- `ct-remove` - Fired when X clicked (if removable) **Slots**: Default slot for + badge text **Example**: + +```html +Status +``` + +### 14. ct-alert + +**Purpose**: Alert message display **Tag**: `` **Attributes**: + +- `variant` - "default" | "destructive" +- `dismissible` - boolean **Events**: +- `ct-dismiss` - Fired when dismissed **Slots**: +- `icon` - Alert icon +- `title` - Alert title +- `description` - Alert description +- Default slot - Alert content **Example**: + +```html + + ⚠️ +

Error

+

Something went wrong

+
+``` + +### 15. ct-separator + +**Purpose**: Visual divider **Tag**: `` **Attributes**: + +- `orientation` - "horizontal" | "vertical" +- `decorative` - boolean **Example**: + +```html + +``` + +### 16. ct-progress + +**Purpose**: Progress indicator **Tag**: `` **Attributes**: + +- `value` - number (0-100) +- `max` - number (default: 100) +- `indeterminate` - boolean **Example**: + +```html + +``` + +### 17. ct-skeleton + +**Purpose**: Loading placeholder **Tag**: `` **Attributes**: None +(style with CSS width/height) **Example**: + +```html + +``` + +### 18. ct-accordion + +**Purpose**: Collapsible content panels **Tag**: `` +**Attributes**: + +- `type` - "single" | "multiple" +- `value` - string | string[] (open items) +- `collapsible` - boolean (for single type) **Events**: +- `ct-change` - Fired on expand/collapse with detail: `{ value }` **Slots**: + Default slot for ct-accordion-item elements **Example**: + +```html + + +
Section 1
+
Content 1
+
+
+``` + +### 19. ct-accordion-item + +**Purpose**: Individual accordion panel **Tag**: `` +**Attributes**: + +- `value` - string (required, unique identifier) +- `disabled` - boolean **Slots**: +- `trigger` - Clickable header +- `content` - Collapsible content + +### 20. ct-collapsible + +**Purpose**: Single collapsible section **Tag**: `` +**Attributes**: + +- `open` - boolean +- `disabled` - boolean **Events**: +- `ct-toggle` - Fired on open/close with detail: `{ open }` **Slots**: +- `trigger` - Clickable trigger element +- `content` - Collapsible content + +### 21. ct-tabs + +**Purpose**: Tabbed interface container **Tag**: `` **Attributes**: + +- `default-value` - string (initially active tab) +- `orientation` - "horizontal" | "vertical" **Events**: +- `ct-change` - Fired on tab change with detail: `{ value }` **Slots**: Default + slot for ct-tab-list and ct-tab-panel elements **Example**: + +```html + + + Tab 1 + Tab 2 + + Content 1 + Content 2 + +``` + +### 22. ct-tab-list + +**Purpose**: Container for tab buttons **Tag**: `` **Slots**: +Default slot for ct-tab elements + +### 23. ct-tab + +**Purpose**: Individual tab button **Tag**: `` **Attributes**: + +- `value` - string (required) +- `disabled` - boolean **Events**: +- `click` - Native click event + +### 24. ct-tab-panel + +**Purpose**: Tab content panel **Tag**: `` **Attributes**: + +- `value` - string (required, matches tab value) **Slots**: Default slot for + content + +### 25. ct-scroll-area + +**Purpose**: Custom scrollable area **Tag**: `` **Attributes**: + +- `orientation` - "vertical" | "horizontal" | "both" **Slots**: Default slot for + scrollable content **Example**: + +```html + +
Long content...
+
+``` + +### 26. ct-aspect-ratio + +**Purpose**: Maintains aspect ratio of content **Tag**: `` +**Attributes**: + +- `ratio` - string (e.g., "16/9", "1/1", "4/3") **Slots**: Default slot for + content **Example**: + +```html + +
Video placeholder
+
+``` + +### 27. ct-form + +**Purpose**: Form wrapper with validation **Tag**: `` **Attributes**: + +- `action` - string +- `method` - string +- `novalidate` - boolean **Events**: +- `ct-submit` - Fired on valid submission with detail: `{ formData }` +- `ct-invalid` - Fired on validation failure with detail: `{ errors }` + +**Slots**: Default slot for form elements **Methods**: + +- `submit()` - Programmatically submit +- `reset()` - Reset form +- `validate()` - Validate and return boolean + +### 28. ct-input-otp + +**Purpose**: One-time password input **Tag**: `` **Attributes**: + +- `length` - number (default: 6) +- `value` - string +- `disabled` - boolean +- `name` - string **Events**: +- `ct-change` - Fired on value change with detail: `{ value, complete }` +- `ct-complete` - Fired when all digits entered with detail: `{ value }` + +**Methods**: + +- `focus()` - Focus first input +- `clear()` - Clear all inputs **Example**: + +```html + +``` + +### 29. ct-resizable-panel-group + +**Purpose**: Container for resizable panels **Tag**: +`` **Attributes**: + +- `direction` - "horizontal" | "vertical" **Events**: +- `ct-layout` - Fired on resize with detail: `{ sizes }` **Slots**: Default slot + for panels and handles + +### 30. ct-resizable-panel + +**Purpose**: Individual resizable panel **Tag**: `` +**Attributes**: + +- `default-size` - number (percentage) +- `min-size` - number (percentage) +- `max-size` - number (percentage) +- `collapsible` - boolean **Slots**: Default slot for content + +### 31. ct-resizable-handle + +**Purpose**: Drag handle between panels **Tag**: `` +**Attributes**: None + +## Layout Components + +### 32. ct-hstack + +**Purpose**: Horizontal flexbox container **Tag**: `` **Attributes**: + +- `gap` - "0" | "1" | "2" | "3" | "4" | "5" | "6" | "8" +- `align` - "start" | "center" | "end" | "stretch" | "baseline" +- `justify` - "start" | "center" | "end" | "between" | "around" | "evenly" +- `wrap` - boolean +- `reverse` - boolean **Slots**: Default slot for child elements **Example**: + +```html + + Left + Right + +``` + +### 33. ct-vstack + +**Purpose**: Vertical flexbox container **Tag**: `` **Attributes**: +Same as ct-hstack **Example**: + +```html + + Card 1 + Card 2 + +``` + +### 34. ct-hgroup + +**Purpose**: Horizontal group with semantic spacing **Tag**: `` +**Attributes**: + +- `gap` - "xs" | "sm" | "md" | "lg" | "xl" **Slots**: Default slot for grouped + elements + +### 35. ct-vgroup + +**Purpose**: Vertical group with semantic spacing **Tag**: `` +**Attributes**: Same as ct-hgroup + +### 36. ct-hscroll + +**Purpose**: Horizontal scroll container **Tag**: `` **Attributes**: + +- `fade-edges` - boolean (gradient fade on edges) +- `show-scrollbar` - boolean +- `snap` - boolean (scroll snapping) **Events**: +- `ct-scroll` - Fired on scroll with detail: + `{ scrollLeft, scrollWidth, clientWidth }` **Methods**: +- `scrollToX(x, smooth)` - Scroll to position +- `scrollByX(x, smooth)` - Scroll by amount + +### 37. ct-vscroll + +**Purpose**: Vertical scroll container **Tag**: `` **Attributes**: + +- `height` - string (CSS height) +- `fade-edges` - boolean +- `show-scrollbar` - boolean +- `snap` - boolean **Events**: +- `ct-scroll` - Fired on scroll with detail: + `{ scrollTop, scrollHeight, clientHeight }` **Methods**: +- `scrollToY(y, smooth)` - Scroll to position +- `scrollByY(y, smooth)` - Scroll by amount + +### 38. ct-grid + +**Purpose**: CSS Grid container **Tag**: `` **Attributes**: + +- `columns` - number | string (e.g., "3" or "repeat(auto-fit, minmax(200px, + 1fr))") +- `rows` - number | string +- `gap` - "0" | "1" | "2" | "3" | "4" | "5" | "6" | "8" +- `column-gap` - same as gap +- `row-gap` - same as gap +- `areas` - string (grid template areas) +- `auto-flow` - "row" | "column" | "dense" | "row dense" | "column dense" + +**Example**: + +```html + +
Item 1
+
Item 2
+
Item 3
+
+``` + +### 39. ct-table + +**Purpose**: Semantic HTML table **Tag**: `` **Attributes**: + +- `striped` - boolean (zebra stripes) +- `bordered` - boolean +- `hover` - boolean (row hover effect) +- `compact` - boolean (reduced padding) +- `fixed` - boolean (fixed layout) **Slots**: Default slot for thead, tbody, + tfoot **Example**: + +```html + + + + Name + Value + + + + + Item 1 + 100 + + + +``` + +## Component Composition Guidelines + +### Form Example + +```html + + + + Full Name + + + + + Email + + + + + Message + + + + + Cancel + Submit + + + +``` + +### Dashboard Layout Example + +```html + + +

Dashboard

+ + + + Active +

Total Users

+

1,234

+
+
+ +
+
+ + + + Overview + Analytics + Reports + + + + + +
+``` + +## Event Handling Patterns + +All components emit custom events with the `ct-` prefix. Event details are +always in the `detail` property: + +```javascript +document.querySelector("ct-button").addEventListener("ct-click", (e) => { + console.log("Button clicked:", e.detail); +}); + +document.querySelector("ct-input").addEventListener("ct-change", (e) => { + console.log("Input value:", e.detail.value); +}); + +document.querySelector("ct-form").addEventListener("ct-submit", (e) => { + e.preventDefault(); + console.log("Form data:", e.detail.formData); +}); +``` + +## Styling Components + +Components expose CSS custom properties and parts for styling: + +```css +/* Custom properties */ +ct-button { + --background: #3b82f6; + --foreground: white; +} + +/* CSS parts */ +ct-input::part(input) { + font-family: monospace; +} + +ct-card::part(header) { + background: #f3f4f6; +} +``` + +## Security Constraints + +When composing components, remember: + +- No ``, ``, `

Card Title

+

Card content

+
+ + +Legacy Button +``` + +## 📖 V2 Components (39 total) + +### Core UI Components (23) + +- **Forms**: `ct-button`, `ct-input`, `ct-textarea`, `ct-checkbox`, `ct-radio`, + `ct-switch`, `ct-toggle`, `ct-slider` +- **Layout**: `ct-card`, `ct-separator`, `ct-accordion`, `ct-collapsible`, + `ct-tabs`, `ct-scroll-area` +- **Feedback**: `ct-alert`, `ct-badge`, `ct-progress`, `ct-skeleton`, `ct-label` +- **Data**: `ct-table`, `ct-form`, `ct-input-otp` +- **Display**: `ct-aspect-ratio`, `ct-resizable-panel-group` + +### Layout Components (8) + +- **Flexbox**: `ct-hstack`, `ct-vstack`, `ct-hgroup`, `ct-vgroup` +- **Scrolling**: `ct-hscroll`, `ct-vscroll` +- **Grid**: `ct-grid`, `ct-table` + +## 🔒 Security Constraints + +Both v1 and v2 components are designed for secure, sandboxed environments: + +- **No External Resources** - No images, SVGs, or remote fetching +- **DOM Isolation** - Components cannot access DOM outside their Shadow DOM +- **Limited Events** - Only keyboard, mouse, focus, and form events allowed +- **No Navigation** - No anchor tags or external links +- **Visual Containment** - Components render within parent bounds + +## 🤖 LLM Integration + +This library includes `LLM-COMPONENT-INSTRUCTIONS.md`, a comprehensive guide for +Language Models (like Claude, GPT-4) to assist with component composition. The +guide includes: + +- Complete component API reference +- Attribute types and event specifications +- Usage examples for all 39 v2 components +- Security constraints and best practices +- Component composition patterns + +When working with an LLM, reference this file to ensure accurate component +usage. + +## 💻 Development + +### Commands + +```bash +# Type checking +deno task check # Check all files +deno task check:v2 # Check v2 only + +# Linting & Formatting +deno task lint # Lint all files +deno task lint:v2 # Lint v2 only +deno task fmt # Format code + +# Testing +deno task test # Run tests + +# Clean +deno task clean # Remove build artifacts +``` + +### Project Structure Details + +``` +packages/ui/ +├── deno.json # Deno configuration +├── README.md # This file +├── LLM-COMPONENT-INSTRUCTIONS.md # AI assistant guide +├── src/ +│ ├── index.ts # Main exports +│ ├── v1/ # Legacy components +│ │ ├── components/ # common-* components +│ │ └── index.ts +│ └── v2/ # Modern components +│ ├── components/ # ct-* components +│ ├── core/ # BaseElement class +│ ├── styles/ # Shared styles +│ ├── utils/ # Utilities +│ ├── types/ # TypeScript types +│ ├── register-all.ts # Auto-registration +│ └── index.ts +``` + +## 📚 Examples + +### Form with Validation + +```html + + + + Email + + + + + Message + + + + + Cancel + Submit + + + +``` + +### Dashboard Layout + +```html + + +

Dashboard

+ + + + Active +

Total Users

+

1,234

+
+
+ +
+
+
+``` + +### Event Handling + +```javascript +// V2 events (ct- prefix) +document.querySelector("ct-button").addEventListener("ct-click", (e) => { + console.log("Button clicked:", e.detail); +}); + +document.querySelector("ct-form").addEventListener("ct-submit", (e) => { + e.preventDefault(); + console.log("Form data:", e.detail.formData); +}); +``` + +## 🎨 Styling + +Components support CSS custom properties and parts: + +```css +/* Custom properties */ +ct-button { + --background: #3b82f6; + --foreground: white; +} + +/* CSS parts */ +ct-input::part(input) { + font-family: monospace; +} + +ct-card::part(header) { + background: #f3f4f6; +} +``` + +### TypeScript/JSX Support + +For React/TypeScript projects, add type definitions: + +```typescript +// types/jsx.d.ts +declare namespace JSX { + interface IntrinsicElements { + "ct-button": { + variant?: + | "default" + | "destructive" + | "outline" + | "secondary" + | "ghost" + | "link"; + size?: "default" | "sm" | "lg" | "icon"; + disabled?: boolean; + } & React.HTMLAttributes; + // ... other components + } +} +``` + +## 🤝 Contributing + +1. Follow established patterns in `BaseElement` +2. Maintain security constraints +3. Include comprehensive JSDoc documentation +4. Add test files for new components +5. Update type definitions +6. Follow the style guide in existing components + +## 📄 License + +MIT License - see LICENSE file for details + +## 🙏 Acknowledgments + +- v2 design system based on [shadcn/ui](https://ui.shadcn.com/) +- Built with [Lit](https://lit.dev/) web components +- Optimized for [Deno](https://deno.land/) runtime +- Secured for sandboxed environments diff --git a/packages/ui/deno.json b/packages/ui/deno.json index fcd648e21..06a0f8dff 100644 --- a/packages/ui/deno.json +++ b/packages/ui/deno.json @@ -1,6 +1,10 @@ { "name": "@commontools/ui", - "exports": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./v1": "./src/v1/index.ts", + "./v2": "./src/v2/index.ts" + }, "tasks": { "bundle": "../../scripts/bundle.ts src/index.ts", "test": "echo 'No tests to run.'" diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index d581677ba..27a7f259a 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,5 +1,12 @@ -export * as components from "./components/index.ts"; -export * as style from "./components/style.ts"; -import { setupShoelace } from "./components/shoelace/index.ts"; +/** + * Common UI Web Components Library + * + * Main entry point that provides access to both v1 and v2 components + */ -setupShoelace(); +// Export v1 and v2 as namespaces +export * as v1 from "./v1/index.ts"; +export * as v2 from "./v2/index.ts"; + +// Export v2 as default (new components) +export * from "./v2/index.ts"; diff --git a/packages/ui/src/components/common-audio-recorder.ts b/packages/ui/src/v1/components/common-audio-recorder.ts similarity index 94% rename from packages/ui/src/components/common-audio-recorder.ts rename to packages/ui/src/v1/components/common-audio-recorder.ts index 17238a021..37d5d436f 100644 --- a/packages/ui/src/components/common-audio-recorder.ts +++ b/packages/ui/src/v1/components/common-audio-recorder.ts @@ -127,16 +127,16 @@ export class CommonAudioRecorderElement extends LitElement { override render() { return html` -