a2uink 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc.cjs +21 -0
  3. package/.gitattributes +5 -0
  4. package/.github/copilot-instructions.md +21 -0
  5. package/.github/workflows/ci.yml +31 -0
  6. package/.husky/pre-commit +6 -0
  7. package/.prettierignore +6 -0
  8. package/.prettierrc +7 -0
  9. package/README.md +44 -0
  10. package/dist/binding.d.ts +3 -0
  11. package/dist/binding.js +73 -0
  12. package/dist/catalog.d.ts +6 -0
  13. package/dist/catalog.js +165 -0
  14. package/dist/examples/demo.d.ts +1 -0
  15. package/dist/examples/demo.js +309 -0
  16. package/dist/focus.d.ts +15 -0
  17. package/dist/focus.js +68 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +1 -0
  20. package/dist/renderer.d.ts +6 -0
  21. package/dist/renderer.js +144 -0
  22. package/dist/src/binding.d.ts +8 -0
  23. package/dist/src/binding.js +141 -0
  24. package/dist/src/catalog.d.ts +2 -0
  25. package/dist/src/catalog.js +1 -0
  26. package/dist/src/components/Box.d.ts +6 -0
  27. package/dist/src/components/Box.js +23 -0
  28. package/dist/src/components/Button.d.ts +7 -0
  29. package/dist/src/components/Button.js +71 -0
  30. package/dist/src/components/Chart.d.ts +5 -0
  31. package/dist/src/components/Chart.js +65 -0
  32. package/dist/src/components/Checkbox.d.ts +7 -0
  33. package/dist/src/components/Checkbox.js +51 -0
  34. package/dist/src/components/DateTimeInput.d.ts +1 -0
  35. package/dist/src/components/DateTimeInput.js +1 -0
  36. package/dist/src/components/Divider.d.ts +5 -0
  37. package/dist/src/components/Divider.js +7 -0
  38. package/dist/src/components/Image.d.ts +5 -0
  39. package/dist/src/components/Image.js +8 -0
  40. package/dist/src/components/Input.d.ts +7 -0
  41. package/dist/src/components/Input.js +124 -0
  42. package/dist/src/components/List.d.ts +5 -0
  43. package/dist/src/components/List.js +9 -0
  44. package/dist/src/components/Modal.d.ts +6 -0
  45. package/dist/src/components/Modal.js +13 -0
  46. package/dist/src/components/RadioGroup.d.ts +7 -0
  47. package/dist/src/components/RadioGroup.js +56 -0
  48. package/dist/src/components/Select.d.ts +7 -0
  49. package/dist/src/components/Select.js +66 -0
  50. package/dist/src/components/Slider.d.ts +7 -0
  51. package/dist/src/components/Slider.js +74 -0
  52. package/dist/src/components/Spacer.d.ts +1 -0
  53. package/dist/src/components/Spacer.js +1 -0
  54. package/dist/src/components/Table.d.ts +5 -0
  55. package/dist/src/components/Table.js +14 -0
  56. package/dist/src/components/Tabs.d.ts +7 -0
  57. package/dist/src/components/Tabs.js +56 -0
  58. package/dist/src/components/Text.d.ts +5 -0
  59. package/dist/src/components/Text.js +15 -0
  60. package/dist/src/components/helpers.d.ts +4 -0
  61. package/dist/src/components/helpers.js +39 -0
  62. package/dist/src/components/index.d.ts +16 -0
  63. package/dist/src/components/index.js +15 -0
  64. package/dist/src/components/renderNode.d.ts +4 -0
  65. package/dist/src/components/renderNode.js +61 -0
  66. package/dist/src/components/types.d.ts +7 -0
  67. package/dist/src/components/types.js +1 -0
  68. package/dist/src/components.d.ts +1 -0
  69. package/dist/src/components.js +1 -0
  70. package/dist/src/focus.d.ts +15 -0
  71. package/dist/src/focus.js +68 -0
  72. package/dist/src/index.d.ts +2 -0
  73. package/dist/src/index.js +1 -0
  74. package/dist/src/renderer.d.ts +6 -0
  75. package/dist/src/renderer.js +673 -0
  76. package/dist/src/tree.d.ts +2 -0
  77. package/dist/src/tree.js +47 -0
  78. package/dist/src/types.d.ts +92 -0
  79. package/dist/src/types.js +1 -0
  80. package/dist/tree.d.ts +2 -0
  81. package/dist/tree.js +45 -0
  82. package/dist/types.d.ts +73 -0
  83. package/dist/types.js +1 -0
  84. package/docs/demo/README.md +90 -0
  85. package/docs/demo/app.js +268 -0
  86. package/docs/demo/index.html +14 -0
  87. package/docs/demo/package-lock.json +4512 -0
  88. package/docs/demo/package.json +32 -0
  89. package/docs/demo/src/App.tsx +1403 -0
  90. package/docs/demo/src/main.tsx +9 -0
  91. package/docs/demo/src/setEnv.ts +29 -0
  92. package/docs/demo/src/shims/fs.js +16 -0
  93. package/docs/demo/src/shims/process.js +10 -0
  94. package/docs/demo/src/styles.css +720 -0
  95. package/docs/demo/styles.css +1 -0
  96. package/docs/demo/tsconfig.json +14 -0
  97. package/docs/demo/vite-plugin-node-polyfills/shims/buffer +2 -0
  98. package/docs/demo/vite-plugin-node-polyfills/shims/global +2 -0
  99. package/docs/demo/vite-plugin-node-polyfills/shims/process +10 -0
  100. package/docs/demo/vite.config.js +200 -0
  101. package/docs/overview.md +277 -0
  102. package/examples/demo.d.ts +1 -0
  103. package/examples/demo.js +66 -0
  104. package/examples/demo.ts +315 -0
  105. package/package.json +48 -0
  106. package/src/binding.ts +191 -0
  107. package/src/catalog.ts +2 -0
  108. package/src/components/Box.ts +39 -0
  109. package/src/components/Button.ts +84 -0
  110. package/src/components/Checkbox.ts +66 -0
  111. package/src/components/DateTimeInput.ts +1 -0
  112. package/src/components/Divider.ts +8 -0
  113. package/src/components/Image.ts +15 -0
  114. package/src/components/Input.ts +148 -0
  115. package/src/components/List.ts +15 -0
  116. package/src/components/Modal.ts +21 -0
  117. package/src/components/RadioGroup.ts +77 -0
  118. package/src/components/Select.ts +94 -0
  119. package/src/components/Slider.ts +98 -0
  120. package/src/components/Spacer.ts +1 -0
  121. package/src/components/Table.ts +22 -0
  122. package/src/components/Tabs.ts +82 -0
  123. package/src/components/Text.ts +21 -0
  124. package/src/components/helpers.ts +42 -0
  125. package/src/components/index.ts +16 -0
  126. package/src/components/renderNode.ts +73 -0
  127. package/src/components/types.ts +8 -0
  128. package/src/components.ts +1 -0
  129. package/src/focus.ts +94 -0
  130. package/src/index.ts +12 -0
  131. package/src/renderer.ts +779 -0
  132. package/src/tree.ts +63 -0
  133. package/src/types.ts +110 -0
  134. package/tsconfig.json +16 -0
@@ -0,0 +1,47 @@
1
+ import { resolveBoundValue, resolvePropsWithBindings } from "./binding.js";
2
+ export function buildResolvedTree(components, rootId, dataModel) {
3
+ const root = components[rootId];
4
+ if (!root) {
5
+ return null;
6
+ }
7
+ return buildNode(root, components, dataModel, undefined, rootId);
8
+ }
9
+ function buildNode(component, components, dataModel, context, instanceKey) {
10
+ var _a, _b, _c;
11
+ const { props, boundProps } = resolvePropsWithBindings((_a = component.props) !== null && _a !== void 0 ? _a : {}, dataModel, context);
12
+ const children = [];
13
+ if ((_b = component.children) === null || _b === void 0 ? void 0 : _b.explicitList) {
14
+ for (const childId of component.children.explicitList) {
15
+ const child = components[childId];
16
+ if (!child) {
17
+ continue;
18
+ }
19
+ children.push(buildNode(child, components, dataModel, context, childId));
20
+ }
21
+ }
22
+ if ((_c = component.children) === null || _c === void 0 ? void 0 : _c.template) {
23
+ const template = component.children.template;
24
+ const binding = typeof template.dataBinding === "string" ? { path: template.dataBinding } : template.dataBinding;
25
+ const items = resolveBoundValue(binding, dataModel, context);
26
+ if (Array.isArray(items)) {
27
+ items.forEach((item, index) => {
28
+ const templateComponent = components[template.componentId];
29
+ if (!templateComponent) {
30
+ return;
31
+ }
32
+ const childContext = { item, index, parent: context };
33
+ const key = `${template.componentId}__${index}`;
34
+ children.push(buildNode(templateComponent, components, dataModel, childContext, key));
35
+ });
36
+ }
37
+ }
38
+ return {
39
+ id: component.id,
40
+ type: component.type,
41
+ props,
42
+ boundProps,
43
+ children,
44
+ instanceKey,
45
+ bindingContext: context
46
+ };
47
+ }
@@ -0,0 +1,92 @@
1
+ export type A2uiServerMessage = BeginRenderingMessage | SurfaceUpdateMessage | DataModelUpdateMessage | DeleteSurfaceMessage;
2
+ export interface BeginRenderingMessage {
3
+ type: "beginRendering";
4
+ surfaceId: string;
5
+ catalogId?: string;
6
+ }
7
+ export interface SurfaceUpdateMessage {
8
+ type: "surfaceUpdate";
9
+ surfaceId: string;
10
+ rootComponentId: string;
11
+ components: Array<ComponentDef | A2uiComponentInstance>;
12
+ }
13
+ export interface DataModelUpdateMessage {
14
+ type: "dataModelUpdate";
15
+ surfaceId: string;
16
+ dataModel?: Record<string, unknown>;
17
+ path?: string;
18
+ contents?: DataModelEntry[];
19
+ }
20
+ export interface DeleteSurfaceMessage {
21
+ type: "deleteSurface";
22
+ surfaceId: string;
23
+ }
24
+ export interface A2uiUserAction {
25
+ type: "userAction";
26
+ surfaceId: string;
27
+ componentId: string;
28
+ actionId: string;
29
+ context?: Record<string, unknown>;
30
+ value?: unknown;
31
+ }
32
+ export interface RendererOptions {
33
+ stdout?: NodeJS.WriteStream;
34
+ stderr?: NodeJS.WriteStream;
35
+ stdin?: NodeJS.ReadStream;
36
+ exitOnCtrlC?: boolean;
37
+ patchConsole?: boolean;
38
+ onUserAction?: (action: A2uiUserAction) => void;
39
+ }
40
+ export interface ComponentDef {
41
+ id: string;
42
+ type: string;
43
+ props?: Record<string, unknown>;
44
+ children?: ChildrenDef;
45
+ }
46
+ export interface A2uiComponentInstance {
47
+ id: string;
48
+ component: Record<string, unknown>;
49
+ }
50
+ export interface ChildrenDef {
51
+ explicitList?: string[];
52
+ template?: {
53
+ componentId: string;
54
+ dataBinding: BoundValue | string;
55
+ };
56
+ }
57
+ export interface DataModelEntry {
58
+ key: string;
59
+ valueString?: string;
60
+ valueNumber?: number;
61
+ valueBoolean?: boolean;
62
+ valueNull?: boolean;
63
+ valueMap?: DataModelEntry[];
64
+ valueArray?: Array<DataModelEntry | unknown>;
65
+ value?: unknown;
66
+ }
67
+ export type BoundValue = {
68
+ path?: string;
69
+ literalString?: string;
70
+ literalNumber?: number;
71
+ literalBoolean?: boolean;
72
+ literalObject?: Record<string, unknown>;
73
+ literalArray?: unknown[];
74
+ };
75
+ export interface ActionDef {
76
+ actionId: string;
77
+ context?: Record<string, unknown>;
78
+ }
79
+ export interface BindingContext {
80
+ item?: unknown;
81
+ index?: number;
82
+ parent?: BindingContext;
83
+ }
84
+ export interface ResolvedNode {
85
+ id: string;
86
+ type: string;
87
+ props: Record<string, unknown>;
88
+ boundProps?: Record<string, BoundValue>;
89
+ children: ResolvedNode[];
90
+ instanceKey: string;
91
+ bindingContext?: BindingContext;
92
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/tree.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { ComponentDef, ResolvedNode } from "./types.js";
2
+ export declare function buildResolvedTree(components: Record<string, ComponentDef>, rootId: string, dataModel: Record<string, unknown>): ResolvedNode | null;
package/dist/tree.js ADDED
@@ -0,0 +1,45 @@
1
+ import { resolveBoundValue, resolveProps } from "./binding.js";
2
+ export function buildResolvedTree(components, rootId, dataModel) {
3
+ const root = components[rootId];
4
+ if (!root) {
5
+ return null;
6
+ }
7
+ return buildNode(root, components, dataModel, undefined, rootId);
8
+ }
9
+ function buildNode(component, components, dataModel, context, instanceKey) {
10
+ var _a, _b, _c;
11
+ const props = resolveProps((_a = component.props) !== null && _a !== void 0 ? _a : {}, dataModel, context);
12
+ const children = [];
13
+ if ((_b = component.children) === null || _b === void 0 ? void 0 : _b.explicitList) {
14
+ for (const childId of component.children.explicitList) {
15
+ const child = components[childId];
16
+ if (!child) {
17
+ continue;
18
+ }
19
+ children.push(buildNode(child, components, dataModel, context, childId));
20
+ }
21
+ }
22
+ if ((_c = component.children) === null || _c === void 0 ? void 0 : _c.template) {
23
+ const template = component.children.template;
24
+ const items = resolveBoundValue(template.dataBinding, dataModel, context);
25
+ if (Array.isArray(items)) {
26
+ items.forEach((item, index) => {
27
+ const templateComponent = components[template.componentId];
28
+ if (!templateComponent) {
29
+ return;
30
+ }
31
+ const childContext = { item, index, parent: context };
32
+ const key = `${template.componentId}__${index}`;
33
+ children.push(buildNode(templateComponent, components, dataModel, childContext, key));
34
+ });
35
+ }
36
+ }
37
+ return {
38
+ id: component.id,
39
+ type: component.type,
40
+ props,
41
+ children,
42
+ instanceKey,
43
+ bindingContext: context
44
+ };
45
+ }
@@ -0,0 +1,73 @@
1
+ export type A2uiServerMessage = BeginRenderingMessage | SurfaceUpdateMessage | DataModelUpdateMessage | DeleteSurfaceMessage;
2
+ export interface BeginRenderingMessage {
3
+ type: "beginRendering";
4
+ surfaceId: string;
5
+ catalogId?: string;
6
+ }
7
+ export interface SurfaceUpdateMessage {
8
+ type: "surfaceUpdate";
9
+ surfaceId: string;
10
+ rootComponentId: string;
11
+ components: ComponentDef[];
12
+ }
13
+ export interface DataModelUpdateMessage {
14
+ type: "dataModelUpdate";
15
+ surfaceId: string;
16
+ dataModel: Record<string, unknown>;
17
+ }
18
+ export interface DeleteSurfaceMessage {
19
+ type: "deleteSurface";
20
+ surfaceId: string;
21
+ }
22
+ export interface A2uiUserAction {
23
+ type: "userAction";
24
+ surfaceId: string;
25
+ componentId: string;
26
+ actionId: string;
27
+ context?: Record<string, unknown>;
28
+ value?: unknown;
29
+ }
30
+ export interface RendererOptions {
31
+ stdout?: NodeJS.WriteStream;
32
+ stderr?: NodeJS.WriteStream;
33
+ stdin?: NodeJS.ReadStream;
34
+ onUserAction?: (action: A2uiUserAction) => void;
35
+ }
36
+ export interface ComponentDef {
37
+ id: string;
38
+ type: string;
39
+ props?: Record<string, unknown>;
40
+ children?: ChildrenDef;
41
+ }
42
+ export interface ChildrenDef {
43
+ explicitList?: string[];
44
+ template?: {
45
+ componentId: string;
46
+ dataBinding: BoundValue;
47
+ };
48
+ }
49
+ export type BoundValue = {
50
+ path?: string;
51
+ literalString?: string;
52
+ literalNumber?: number;
53
+ literalBoolean?: boolean;
54
+ literalObject?: Record<string, unknown>;
55
+ literalArray?: unknown[];
56
+ };
57
+ export interface ActionDef {
58
+ actionId: string;
59
+ context?: Record<string, unknown>;
60
+ }
61
+ export interface BindingContext {
62
+ item?: unknown;
63
+ index?: number;
64
+ parent?: BindingContext;
65
+ }
66
+ export interface ResolvedNode {
67
+ id: string;
68
+ type: string;
69
+ props: Record<string, unknown>;
70
+ children: ResolvedNode[];
71
+ instanceKey: string;
72
+ bindingContext?: BindingContext;
73
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,90 @@
1
+ # A2UI Ink Demo Site
2
+
3
+ A Vite-powered demo page and integration guide for the A2UI Ink library.
4
+
5
+ ## Features
6
+
7
+ - **Component Catalog**: Browse all available components with examples and prop documentation
8
+ - **Interactive Terminal**: Web-based terminal preview using xterm.js
9
+ - **JSON Playground**: Paste your A2UI JSON and see it rendered in real-time (full or simple mode)
10
+ - **Integration Guide**: Step-by-step instructions for getting started
11
+
12
+ ## Running Locally
13
+
14
+ This demo is a Vite app with Ink running in a browser-based xterm instance.
15
+
16
+ ```bash
17
+ cd docs/demo
18
+ npm install
19
+ npm run dev
20
+ ```
21
+
22
+ Build for static deployment:
23
+
24
+ ```bash
25
+ npm run build
26
+ npm run preview
27
+ ```
28
+
29
+ Run the TypeScript typecheck:
30
+
31
+ ```bash
32
+ npm run typecheck
33
+ ```
34
+
35
+ ## Structure
36
+
37
+ ```
38
+ docs/demo/
39
+ ├── index.html # Main HTML page
40
+ ├── vite.config.js # Vite configuration (Node polyfills)
41
+ ├── package.json # Demo dependencies and scripts
42
+ ├── src/
43
+ │ ├── App.tsx # Demo UI + playground
44
+ │ ├── main.tsx # React entrypoint
45
+ │ └── styles.css # Styling
46
+ └── README.md # This file
47
+ ```
48
+
49
+ ## Dependencies
50
+
51
+ - [ink](https://github.com/vadimdemedes/ink) - React renderer for terminal UIs
52
+ - [xterm.js](https://xtermjs.org/) - Terminal emulation
53
+ - [vite-plugin-node-polyfills](https://github.com/davidmyersdev/vite-plugin-node-polyfills) - Node stream polyfills
54
+
55
+ ## Customization
56
+
57
+ ### Adding New Components
58
+
59
+ 1. Add a new section in `src/App.tsx` following the existing pattern
60
+ 2. Update the example JSON in `src/App.tsx`
61
+ 3. Update the navigation in the sidebar
62
+
63
+ ### Simple Input Mode
64
+
65
+ The playground includes a **Simple Mode** toggle that lets users paste:
66
+
67
+ - A components array (no surface metadata)
68
+ - A separate dataModel object
69
+
70
+ The demo uses a fixed surface ID (`demo`) and root component ID (`root`) in this mode.
71
+
72
+ ### Theming
73
+
74
+ Edit the CSS variables in `src/styles.css`:
75
+
76
+ ```css
77
+ :root {
78
+ --bg-primary: #1a1a2e;
79
+ --accent-primary: #00d9ff;
80
+ /* ... */
81
+ }
82
+ ```
83
+
84
+ ## Browser Support
85
+
86
+ Works in all modern browsers that support:
87
+
88
+ - ES6+ JavaScript
89
+ - CSS Grid & Flexbox
90
+ - WebGL (for xterm.js performance)
@@ -0,0 +1,268 @@
1
+ // A2UI Ink Demo - Interactive Playground
2
+ (function() {
3
+ 'use strict';
4
+
5
+ // DOM Elements
6
+ const navLinks = document.querySelectorAll('.nav-link');
7
+ const sections = document.querySelectorAll('.section');
8
+ const jsonEditor = document.getElementById('jsonEditor');
9
+ const renderBtn = document.getElementById('renderBtn');
10
+ const loadExampleBtn = document.getElementById('loadExample');
11
+ const clearTerminalBtn = document.getElementById('clearTerminal');
12
+ const actionsLog = document.getElementById('actionsLog');
13
+ const terminalContainer = document.getElementById('terminal');
14
+
15
+ // Terminal instance
16
+ let term = null;
17
+ let fitAddon = null;
18
+ let commandBuffer = '';
19
+ let currentJson = null;
20
+ const promptText = '\x1b[90ma2ui> \x1b[0m';
21
+
22
+ // Initialize
23
+ function init() {
24
+ setupNavigation();
25
+ setupTerminal();
26
+ setupPlayground();
27
+
28
+ // Handle hash navigation
29
+ if (window.location.hash) {
30
+ navigateToSection(window.location.hash.slice(1));
31
+ }
32
+ }
33
+
34
+ // Navigation
35
+ function setupNavigation() {
36
+ navLinks.forEach(link => {
37
+ link.addEventListener('click', (e) => {
38
+ e.preventDefault();
39
+ const sectionId = link.dataset.section;
40
+ navigateToSection(sectionId);
41
+ window.history.pushState(null, '', `#${sectionId}`);
42
+ });
43
+ });
44
+
45
+ window.addEventListener('popstate', () => {
46
+ if (window.location.hash) {
47
+ navigateToSection(window.location.hash.slice(1));
48
+ }
49
+ });
50
+ }
51
+
52
+ function navigateToSection(sectionId) {
53
+ // Update nav
54
+ navLinks.forEach(link => {
55
+ link.classList.toggle('active', link.dataset.section === sectionId);
56
+ });
57
+
58
+ // Update sections
59
+ sections.forEach(section => {
60
+ // Legacy script removed. Vite entry is src/main.js
61
+ if ('literalObject' in value) return value.literalObject;
62
+
63
+ if ('path' in value) {
64
+ const result = getValueByPath(dataModel, value.path);
65
+ if (result !== undefined) return result;
66
+ // Fall back to literal if path doesn't resolve
67
+ if ('literalString' in value) return value.literalString;
68
+ if ('literalNumber' in value) return value.literalNumber;
69
+ if ('literalBoolean' in value) return value.literalBoolean;
70
+ return undefined;
71
+ }
72
+
73
+ if ('actionId' in value) return value;
74
+
75
+ return value;
76
+ }
77
+
78
+ function getValueByPath(obj, path) {
79
+ if (!path) return obj;
80
+ const parts = path.split('.');
81
+ let current = obj;
82
+ for (const part of parts) {
83
+ if (current === null || current === undefined) return undefined;
84
+ current = current[part];
85
+ }
86
+ return current;
87
+ }
88
+
89
+ function setValueByPath(obj, path, value) {
90
+ const parts = path.split('.');
91
+ let current = obj;
92
+ for (let i = 0; i < parts.length - 1; i++) {
93
+ const part = parts[i];
94
+ if (typeof current[part] !== 'object' || current[part] === null) {
95
+ current[part] = {};
96
+ }
97
+ current = current[part];
98
+ }
99
+ current[parts[parts.length - 1]] = value;
100
+ }
101
+
102
+ function getChildren(component, componentMap) {
103
+ if (!component.children) return [];
104
+ if (component.children.explicitList) {
105
+ return component.children.explicitList
106
+ .map(id => componentMap[id])
107
+ .filter(Boolean);
108
+ }
109
+ return [];
110
+ }
111
+
112
+ function getBorderChars(style) {
113
+ const styles = {
114
+ single: { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│' },
115
+ double: { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║' },
116
+ round: { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' },
117
+ bold: { tl: '┏', tr: '┓', bl: '┗', br: '┛', h: '━', v: '┃' }
118
+ };
119
+ return styles[style] || styles.single;
120
+ }
121
+
122
+ function getColorCode(color) {
123
+ const colors = {
124
+ black: '\x1b[30m',
125
+ red: '\x1b[31m',
126
+ green: '\x1b[32m',
127
+ yellow: '\x1b[33m',
128
+ blue: '\x1b[34m',
129
+ magenta: '\x1b[35m',
130
+ cyan: '\x1b[36m',
131
+ white: '\x1b[37m'
132
+ };
133
+ return colors[color] || '';
134
+ }
135
+
136
+ function logAction(type, componentId, data = {}) {
137
+ if (!actionsLog) return;
138
+
139
+ // Remove placeholder if present
140
+ const placeholder = actionsLog.querySelector('.placeholder');
141
+ if (placeholder) placeholder.remove();
142
+
143
+ const entry = document.createElement('div');
144
+ entry.className = 'action-entry';
145
+ entry.innerHTML = `
146
+ <span class="action-type">${type}</span> →
147
+ <span class="action-component">${componentId}</span>
148
+ ${Object.keys(data).length ? ` <code>${JSON.stringify(data)}</code>` : ''}
149
+ `;
150
+ actionsLog.appendChild(entry);
151
+ actionsLog.scrollTop = actionsLog.scrollHeight;
152
+ }
153
+
154
+ function loadExample() {
155
+ const example = {
156
+ "surfaceId": "demo",
157
+ "rootComponentId": "root",
158
+ "components": [
159
+ {
160
+ "id": "root",
161
+ "type": "Box",
162
+ "props": {
163
+ "direction": "column",
164
+ "padding": 1,
165
+ "borderStyle": "round"
166
+ },
167
+ "children": {
168
+ "explicitList": ["title", "spacer1", "inputSection", "spacer2", "statusSection"]
169
+ }
170
+ },
171
+ {
172
+ "id": "title",
173
+ "type": "Text",
174
+ "props": {
175
+ "text": { "path": "title" },
176
+ "bold": true,
177
+ "color": "cyan"
178
+ }
179
+ },
180
+ { "id": "spacer1", "type": "Spacer" },
181
+ {
182
+ "id": "inputSection",
183
+ "type": "Box",
184
+ "props": { "direction": "column" },
185
+ "children": { "explicitList": ["nameLabel", "nameInput", "checkbox"] }
186
+ },
187
+ {
188
+ "id": "nameLabel",
189
+ "type": "Text",
190
+ "props": { "text": { "literalString": "Enter your name:" } }
191
+ },
192
+ {
193
+ "id": "nameInput",
194
+ "type": "Input",
195
+ "props": {
196
+ "value": { "path": "form.name" },
197
+ "placeholder": "Your name here",
198
+ "onChange": { "actionId": "nameChange" }
199
+ }
200
+ },
201
+ {
202
+ "id": "checkbox",
203
+ "type": "Checkbox",
204
+ "props": {
205
+ "label": "Subscribe to newsletter",
206
+ "checked": { "path": "form.subscribe" },
207
+ "onChange": { "actionId": "toggleSubscribe" }
208
+ }
209
+ },
210
+ { "id": "spacer2", "type": "Spacer" },
211
+ {
212
+ "id": "statusSection",
213
+ "type": "Box",
214
+ "props": { "direction": "column" },
215
+ "children": { "explicitList": ["statusLabel", "statusTable", "submitBtn"] }
216
+ },
217
+ {
218
+ "id": "statusLabel",
219
+ "type": "Text",
220
+ "props": { "text": { "literalString": "System Status:" } }
221
+ },
222
+ {
223
+ "id": "statusTable",
224
+ "type": "Table",
225
+ "props": {
226
+ "columns": { "path": "status.columns" },
227
+ "rows": { "path": "status.rows" }
228
+ }
229
+ },
230
+ {
231
+ "id": "submitBtn",
232
+ "type": "Button",
233
+ "props": {
234
+ "label": { "literalString": "Submit" },
235
+ "onPress": { "actionId": "submit" }
236
+ }
237
+ }
238
+ ],
239
+ "dataModel": {
240
+ "title": "A2UI Demo Form",
241
+ "form": {
242
+ "name": "John Doe",
243
+ "subscribe": true
244
+ },
245
+ "status": {
246
+ "columns": [
247
+ { "key": "service", "header": "Service" },
248
+ { "key": "status", "header": "Status" }
249
+ ],
250
+ "rows": [
251
+ { "service": "API", "status": "Online" },
252
+ { "service": "Database", "status": "Online" },
253
+ { "service": "Cache", "status": "Warning" }
254
+ ]
255
+ }
256
+ }
257
+ };
258
+
259
+ jsonEditor.value = JSON.stringify(example, null, 2);
260
+ }
261
+
262
+ // Initialize on DOM ready
263
+ if (document.readyState === 'loading') {
264
+ document.addEventListener('DOMContentLoaded', init);
265
+ } else {
266
+ init();
267
+ }
268
+ })();
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>A2UI Ink - Interactive Demo & Component Guide</title>
7
+ <link rel="stylesheet" href="/src/styles.css">
8
+ <script type="module" src="/src/main.tsx"></script>
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+
13
+ </body>
14
+ </html>