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.
- package/.eslintignore +4 -0
- package/.eslintrc.cjs +21 -0
- package/.gitattributes +5 -0
- package/.github/copilot-instructions.md +21 -0
- package/.github/workflows/ci.yml +31 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +6 -0
- package/.prettierrc +7 -0
- package/README.md +44 -0
- package/dist/binding.d.ts +3 -0
- package/dist/binding.js +73 -0
- package/dist/catalog.d.ts +6 -0
- package/dist/catalog.js +165 -0
- package/dist/examples/demo.d.ts +1 -0
- package/dist/examples/demo.js +309 -0
- package/dist/focus.d.ts +15 -0
- package/dist/focus.js +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/renderer.d.ts +6 -0
- package/dist/renderer.js +144 -0
- package/dist/src/binding.d.ts +8 -0
- package/dist/src/binding.js +141 -0
- package/dist/src/catalog.d.ts +2 -0
- package/dist/src/catalog.js +1 -0
- package/dist/src/components/Box.d.ts +6 -0
- package/dist/src/components/Box.js +23 -0
- package/dist/src/components/Button.d.ts +7 -0
- package/dist/src/components/Button.js +71 -0
- package/dist/src/components/Chart.d.ts +5 -0
- package/dist/src/components/Chart.js +65 -0
- package/dist/src/components/Checkbox.d.ts +7 -0
- package/dist/src/components/Checkbox.js +51 -0
- package/dist/src/components/DateTimeInput.d.ts +1 -0
- package/dist/src/components/DateTimeInput.js +1 -0
- package/dist/src/components/Divider.d.ts +5 -0
- package/dist/src/components/Divider.js +7 -0
- package/dist/src/components/Image.d.ts +5 -0
- package/dist/src/components/Image.js +8 -0
- package/dist/src/components/Input.d.ts +7 -0
- package/dist/src/components/Input.js +124 -0
- package/dist/src/components/List.d.ts +5 -0
- package/dist/src/components/List.js +9 -0
- package/dist/src/components/Modal.d.ts +6 -0
- package/dist/src/components/Modal.js +13 -0
- package/dist/src/components/RadioGroup.d.ts +7 -0
- package/dist/src/components/RadioGroup.js +56 -0
- package/dist/src/components/Select.d.ts +7 -0
- package/dist/src/components/Select.js +66 -0
- package/dist/src/components/Slider.d.ts +7 -0
- package/dist/src/components/Slider.js +74 -0
- package/dist/src/components/Spacer.d.ts +1 -0
- package/dist/src/components/Spacer.js +1 -0
- package/dist/src/components/Table.d.ts +5 -0
- package/dist/src/components/Table.js +14 -0
- package/dist/src/components/Tabs.d.ts +7 -0
- package/dist/src/components/Tabs.js +56 -0
- package/dist/src/components/Text.d.ts +5 -0
- package/dist/src/components/Text.js +15 -0
- package/dist/src/components/helpers.d.ts +4 -0
- package/dist/src/components/helpers.js +39 -0
- package/dist/src/components/index.d.ts +16 -0
- package/dist/src/components/index.js +15 -0
- package/dist/src/components/renderNode.d.ts +4 -0
- package/dist/src/components/renderNode.js +61 -0
- package/dist/src/components/types.d.ts +7 -0
- package/dist/src/components/types.js +1 -0
- package/dist/src/components.d.ts +1 -0
- package/dist/src/components.js +1 -0
- package/dist/src/focus.d.ts +15 -0
- package/dist/src/focus.js +68 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/renderer.d.ts +6 -0
- package/dist/src/renderer.js +673 -0
- package/dist/src/tree.d.ts +2 -0
- package/dist/src/tree.js +47 -0
- package/dist/src/types.d.ts +92 -0
- package/dist/src/types.js +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.js +45 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.js +1 -0
- package/docs/demo/README.md +90 -0
- package/docs/demo/app.js +268 -0
- package/docs/demo/index.html +14 -0
- package/docs/demo/package-lock.json +4512 -0
- package/docs/demo/package.json +32 -0
- package/docs/demo/src/App.tsx +1403 -0
- package/docs/demo/src/main.tsx +9 -0
- package/docs/demo/src/setEnv.ts +29 -0
- package/docs/demo/src/shims/fs.js +16 -0
- package/docs/demo/src/shims/process.js +10 -0
- package/docs/demo/src/styles.css +720 -0
- package/docs/demo/styles.css +1 -0
- package/docs/demo/tsconfig.json +14 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/buffer +2 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/global +2 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/process +10 -0
- package/docs/demo/vite.config.js +200 -0
- package/docs/overview.md +277 -0
- package/examples/demo.d.ts +1 -0
- package/examples/demo.js +66 -0
- package/examples/demo.ts +315 -0
- package/package.json +48 -0
- package/src/binding.ts +191 -0
- package/src/catalog.ts +2 -0
- package/src/components/Box.ts +39 -0
- package/src/components/Button.ts +84 -0
- package/src/components/Checkbox.ts +66 -0
- package/src/components/DateTimeInput.ts +1 -0
- package/src/components/Divider.ts +8 -0
- package/src/components/Image.ts +15 -0
- package/src/components/Input.ts +148 -0
- package/src/components/List.ts +15 -0
- package/src/components/Modal.ts +21 -0
- package/src/components/RadioGroup.ts +77 -0
- package/src/components/Select.ts +94 -0
- package/src/components/Slider.ts +98 -0
- package/src/components/Spacer.ts +1 -0
- package/src/components/Table.ts +22 -0
- package/src/components/Tabs.ts +82 -0
- package/src/components/Text.ts +21 -0
- package/src/components/helpers.ts +42 -0
- package/src/components/index.ts +16 -0
- package/src/components/renderNode.ts +73 -0
- package/src/components/types.ts +8 -0
- package/src/components.ts +1 -0
- package/src/focus.ts +94 -0
- package/src/index.ts +12 -0
- package/src/renderer.ts +779 -0
- package/src/tree.ts +63 -0
- package/src/types.ts +110 -0
- package/tsconfig.json +16 -0
package/dist/src/tree.js
ADDED
|
@@ -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
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
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -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)
|
package/docs/demo/app.js
ADDED
|
@@ -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>
|