@vemjs/renderer-vecto 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 (40) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +115 -0
  3. package/dist/CommandBar.d.ts +13 -0
  4. package/dist/CommandBar.d.ts.map +1 -0
  5. package/dist/CommandBar.js +60 -0
  6. package/dist/CommandBar.js.map +1 -0
  7. package/dist/FileSystemHandler.d.ts +9 -0
  8. package/dist/FileSystemHandler.d.ts.map +1 -0
  9. package/dist/FileSystemHandler.js +51 -0
  10. package/dist/FileSystemHandler.js.map +1 -0
  11. package/dist/VemEditorEntity.d.ts +16 -0
  12. package/dist/VemEditorEntity.d.ts.map +1 -0
  13. package/dist/VemEditorEntity.js +209 -0
  14. package/dist/VemEditorEntity.js.map +1 -0
  15. package/dist/Workspace.d.ts +13 -0
  16. package/dist/Workspace.d.ts.map +1 -0
  17. package/dist/Workspace.js +55 -0
  18. package/dist/Workspace.js.map +1 -0
  19. package/dist/WorkspaceExplorer.d.ts +18 -0
  20. package/dist/WorkspaceExplorer.d.ts.map +1 -0
  21. package/dist/WorkspaceExplorer.js +102 -0
  22. package/dist/WorkspaceExplorer.js.map +1 -0
  23. package/dist/WorkspaceLayout.d.ts +38 -0
  24. package/dist/WorkspaceLayout.d.ts.map +1 -0
  25. package/dist/WorkspaceLayout.js +165 -0
  26. package/dist/WorkspaceLayout.js.map +1 -0
  27. package/dist/index.d.ts +15 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +41 -0
  30. package/dist/index.js.map +1 -0
  31. package/package.json +19 -0
  32. package/src/CommandBar.ts +69 -0
  33. package/src/FileSystemHandler.test.ts +51 -0
  34. package/src/FileSystemHandler.ts +60 -0
  35. package/src/VemEditorEntity.ts +235 -0
  36. package/src/Workspace.ts +65 -0
  37. package/src/WorkspaceExplorer.ts +124 -0
  38. package/src/WorkspaceLayout.ts +191 -0
  39. package/src/index.ts +48 -0
  40. package/tsconfig.json +13 -0
@@ -0,0 +1,18 @@
1
+ import { UIComponent } from '@vectojs/ui';
2
+ import type { IRenderer } from '@vectojs/core';
3
+ import { VemWorkspace } from './Workspace';
4
+ export declare class WorkspaceExplorer extends UIComponent {
5
+ private panelGroup;
6
+ private leftPanel;
7
+ private rightPanel;
8
+ private workspace;
9
+ private treeView;
10
+ private openBtn;
11
+ private fsHandler;
12
+ constructor(width: number, height: number, initialText?: string);
13
+ getWorkspace(): VemWorkspace;
14
+ private handleOpenFolder;
15
+ update(dt: number, time: number): void;
16
+ render(_r: IRenderer): void;
17
+ }
18
+ //# sourceMappingURL=WorkspaceExplorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceExplorer.d.ts","sourceRoot":"","sources":["../src/WorkspaceExplorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuC,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAoB;gBAEzB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAqCxD,YAAY,IAAI,YAAY;YAIrB,gBAAgB;IAkCvB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAyBtC,MAAM,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;CASnC"}
@@ -0,0 +1,102 @@
1
+ import { UIComponent, PanelGroup, Panel, TreeView, Button } from '@vectojs/ui';
2
+ import { VemWorkspace } from './Workspace';
3
+ import { FileSystemHandler } from './FileSystemHandler';
4
+ export class WorkspaceExplorer extends UIComponent {
5
+ panelGroup;
6
+ leftPanel;
7
+ rightPanel;
8
+ workspace;
9
+ treeView = null;
10
+ openBtn;
11
+ fsHandler;
12
+ constructor(width, height, initialText) {
13
+ super();
14
+ this.width = width;
15
+ this.height = height;
16
+ this.fsHandler = new FileSystemHandler();
17
+ this.panelGroup = new PanelGroup({
18
+ direction: 'horizontal',
19
+ width: width,
20
+ height: height,
21
+ });
22
+ this.leftPanel = new Panel({ minSize: 150, defaultSize: 0.2 });
23
+ this.rightPanel = new Panel({ minSize: 300 });
24
+ this.workspace = new VemWorkspace(width * 0.8, height, initialText);
25
+ this.openBtn = new Button('Open Folder', {
26
+ onClick: () => this.handleOpenFolder(),
27
+ bg: '#1e293b', // slate-800
28
+ hoverBg: '#334155',
29
+ font: '14px monospace',
30
+ color: '#e2e8f0',
31
+ });
32
+ this.openBtn.width = 120;
33
+ this.openBtn.height = 35;
34
+ this.openBtn.setPosition(15, 15);
35
+ this.leftPanel.add(this.openBtn);
36
+ this.rightPanel.add(this.workspace);
37
+ this.panelGroup.addPanel(this.leftPanel);
38
+ this.panelGroup.addPanel(this.rightPanel);
39
+ this.add(this.panelGroup);
40
+ }
41
+ getWorkspace() {
42
+ return this.workspace;
43
+ }
44
+ async handleOpenFolder() {
45
+ if (typeof window === 'undefined' || !window.showDirectoryPicker) {
46
+ console.warn('File System Access API is not supported in this environment.');
47
+ return;
48
+ }
49
+ try {
50
+ const rootHandle = await window.showDirectoryPicker();
51
+ const nodes = await this.fsHandler.readDirectory(rootHandle);
52
+ this.treeView = new TreeView({
53
+ nodes,
54
+ width: this.leftPanel.width,
55
+ height: this.height - 10,
56
+ font: '13px monospace',
57
+ color: '#cbd5e1',
58
+ selectedColor: 'rgba(56, 189, 248, 0.2)',
59
+ hoverColor: 'rgba(255, 255, 255, 0.05)',
60
+ onSelect: async (node) => {
61
+ const fileHandle = this.fsHandler.getFileHandle(node.id);
62
+ if (fileHandle) {
63
+ const content = await this.fsHandler.readFile(fileHandle);
64
+ this.workspace.addTab(content);
65
+ }
66
+ },
67
+ });
68
+ this.leftPanel.remove(this.openBtn);
69
+ this.leftPanel.add(this.treeView);
70
+ }
71
+ catch (err) {
72
+ console.error('Error selecting directory:', err);
73
+ }
74
+ }
75
+ update(dt, time) {
76
+ super.update(dt, time);
77
+ if (this.panelGroup.width !== this.width || this.panelGroup.height !== this.height) {
78
+ this.panelGroup.width = this.width;
79
+ this.panelGroup.height = this.height;
80
+ }
81
+ if (this.treeView &&
82
+ (this.treeView.width !== this.leftPanel.width || this.treeView.height !== this.height)) {
83
+ this.treeView.width = this.leftPanel.width;
84
+ this.treeView.height = this.height;
85
+ }
86
+ if (this.workspace.width !== this.rightPanel.width ||
87
+ this.workspace.height !== this.rightPanel.height) {
88
+ this.workspace.width = this.rightPanel.width;
89
+ this.workspace.height = this.rightPanel.height;
90
+ }
91
+ }
92
+ render(_r) {
93
+ _r.beginPath();
94
+ _r.moveTo(0, 0);
95
+ _r.lineTo(this.leftPanel.width, 0);
96
+ _r.lineTo(this.leftPanel.width, this.height);
97
+ _r.lineTo(0, this.height);
98
+ _r.closePath();
99
+ _r.fill('#090d16'); // deep slate sidebar background
100
+ }
101
+ }
102
+ //# sourceMappingURL=WorkspaceExplorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceExplorer.js","sourceRoot":"","sources":["../src/WorkspaceExplorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { UIComponent, Panel } from '@vectojs/ui';
2
+ import type { IRenderer } from '@vectojs/core';
3
+ import { VemEditorState } from '@vemjs/core';
4
+ import { VemEditorEntity } from './VemEditorEntity';
5
+ export type PaneNode = {
6
+ type: 'leaf';
7
+ id: string;
8
+ state: VemEditorState;
9
+ } | {
10
+ type: 'split';
11
+ id: string;
12
+ direction: 'horizontal' | 'vertical';
13
+ children: PaneNode[];
14
+ };
15
+ export declare class EditorPane extends Panel {
16
+ editorState: VemEditorState;
17
+ editorEntity: VemEditorEntity;
18
+ constructor(state: VemEditorState);
19
+ update(dt: number, time: number): void;
20
+ }
21
+ export declare class WorkspaceLayout extends UIComponent {
22
+ private rootNode;
23
+ private activePaneId;
24
+ private layoutRoot;
25
+ private paneMap;
26
+ constructor(width: number, height: number, initialText?: string);
27
+ private bindStateEvents;
28
+ getActiveState(): VemEditorState | null;
29
+ splitPane(paneId: string, direction: 'horizontal' | 'vertical'): void;
30
+ closePane(paneId: string): void;
31
+ private findParentNode;
32
+ private findFirstLeaf;
33
+ rebuildLayout(): void;
34
+ private buildNode;
35
+ update(dt: number, time: number): void;
36
+ render(_r: IRenderer): void;
37
+ }
38
+ //# sourceMappingURL=WorkspaceLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceLayout.d.ts","sourceRoot":"","sources":["../src/WorkspaceLayout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAU,SAAS,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,YAAY,GAAG,UAAU,CAAC;IAAC,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAE9F,qBAAa,UAAW,SAAQ,KAAK;IAC5B,WAAW,EAAE,cAAc,CAAC;IAC5B,YAAY,EAAE,eAAe,CAAC;gBAEzB,KAAK,EAAE,cAAc;IAO1B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;CAQ9C;AAED,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAA+B;gBAElC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAqB/D,OAAO,CAAC,eAAe;IAShB,cAAc,IAAI,cAAc,GAAG,IAAI;IAQvC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI;IA0BrE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA2BtC,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,aAAa;IAKd,aAAa,IAAI,IAAI;IAS5B,OAAO,CAAC,SAAS;IA0BV,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQtC,MAAM,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;CAGnC"}
@@ -0,0 +1,165 @@
1
+ import { UIComponent, PanelGroup, Panel } from '@vectojs/ui';
2
+ import { VemEditorState } from '@vemjs/core';
3
+ import { VemEditorEntity } from './VemEditorEntity';
4
+ export class EditorPane extends Panel {
5
+ editorState;
6
+ editorEntity;
7
+ constructor(state) {
8
+ super();
9
+ this.editorState = state;
10
+ this.editorEntity = new VemEditorEntity(state);
11
+ this.add(this.editorEntity);
12
+ }
13
+ update(dt, time) {
14
+ super.update(dt, time);
15
+ if (this.editorEntity.width !== this.width || this.editorEntity.height !== this.height) {
16
+ this.editorEntity.width = this.width;
17
+ this.editorEntity.height = this.height;
18
+ this.editorEntity.updateFromState();
19
+ }
20
+ }
21
+ }
22
+ export class WorkspaceLayout extends UIComponent {
23
+ rootNode;
24
+ activePaneId;
25
+ layoutRoot = null;
26
+ paneMap = new Map();
27
+ constructor(width, height, initialText) {
28
+ super();
29
+ this.width = width;
30
+ this.height = height;
31
+ const initialId = 'pane-1';
32
+ const initialState = new VemEditorState(initialText);
33
+ this.rootNode = {
34
+ type: 'leaf',
35
+ id: initialId,
36
+ state: initialState,
37
+ };
38
+ this.activePaneId = initialId;
39
+ this.paneMap.set(initialId, this.rootNode);
40
+ this.bindStateEvents(initialId, initialState);
41
+ this.rebuildLayout();
42
+ }
43
+ bindStateEvents(id, state) {
44
+ state.onSplit((dir) => {
45
+ this.splitPane(id, dir);
46
+ });
47
+ state.onQuit(() => {
48
+ this.closePane(id);
49
+ });
50
+ }
51
+ getActiveState() {
52
+ const pane = this.paneMap.get(this.activePaneId);
53
+ if (pane && pane.type === 'leaf') {
54
+ return pane.state;
55
+ }
56
+ return null;
57
+ }
58
+ splitPane(paneId, direction) {
59
+ const targetNode = this.paneMap.get(paneId);
60
+ if (!targetNode || targetNode.type !== 'leaf')
61
+ return;
62
+ const newId = `pane-${Date.now()}`;
63
+ const newState = new VemEditorState(targetNode.state.getBuffer().getText());
64
+ const newLeaf = {
65
+ type: 'leaf',
66
+ id: newId,
67
+ state: newState,
68
+ };
69
+ this.paneMap.set(newId, newLeaf);
70
+ this.bindStateEvents(newId, newState);
71
+ const oldLeafCopy = { ...targetNode };
72
+ const splitNode = targetNode;
73
+ splitNode.type = 'split';
74
+ splitNode.direction = direction;
75
+ splitNode.children = [oldLeafCopy, newLeaf];
76
+ this.activePaneId = newId;
77
+ this.rebuildLayout();
78
+ }
79
+ closePane(paneId) {
80
+ if (this.rootNode.type === 'leaf')
81
+ return;
82
+ const parentNode = this.findParentNode(this.rootNode, paneId);
83
+ if (!parentNode || parentNode.type !== 'split')
84
+ return;
85
+ const sibling = parentNode.children.find((c) => c.id !== paneId);
86
+ if (!sibling)
87
+ return;
88
+ const grandparent = this.findParentNode(this.rootNode, parentNode.id);
89
+ if (grandparent && grandparent.type === 'split') {
90
+ const idx = grandparent.children.findIndex((c) => c.id === parentNode.id);
91
+ grandparent.children[idx] = sibling;
92
+ }
93
+ else {
94
+ this.rootNode = sibling;
95
+ }
96
+ this.paneMap.delete(paneId);
97
+ const remainingLeaf = this.findFirstLeaf(this.rootNode);
98
+ if (remainingLeaf) {
99
+ this.activePaneId = remainingLeaf.id;
100
+ }
101
+ this.rebuildLayout();
102
+ }
103
+ findParentNode(current, childId) {
104
+ if (current.type === 'leaf')
105
+ return null;
106
+ for (const child of current.children) {
107
+ if (child.id === childId) {
108
+ return current;
109
+ }
110
+ const parent = this.findParentNode(child, childId);
111
+ if (parent)
112
+ return parent;
113
+ }
114
+ return null;
115
+ }
116
+ findFirstLeaf(current) {
117
+ if (current.type === 'leaf')
118
+ return current;
119
+ return this.findFirstLeaf(current.children[0]);
120
+ }
121
+ rebuildLayout() {
122
+ if (this.layoutRoot) {
123
+ this.remove(this.layoutRoot);
124
+ }
125
+ this.layoutRoot = this.buildNode(this.rootNode);
126
+ this.add(this.layoutRoot);
127
+ }
128
+ buildNode(node) {
129
+ if (node.type === 'leaf') {
130
+ const pane = new EditorPane(node.state);
131
+ pane.id = node.id;
132
+ return pane;
133
+ }
134
+ else {
135
+ const group = new PanelGroup({
136
+ direction: node.direction,
137
+ width: this.width,
138
+ height: this.height,
139
+ });
140
+ for (const child of node.children) {
141
+ const childEntity = this.buildNode(child);
142
+ if (childEntity instanceof Panel) {
143
+ group.addPanel(childEntity);
144
+ }
145
+ else {
146
+ const p = new Panel();
147
+ p.add(childEntity);
148
+ group.addPanel(p);
149
+ }
150
+ }
151
+ return group;
152
+ }
153
+ }
154
+ update(dt, time) {
155
+ super.update(dt, time);
156
+ if (this.layoutRoot) {
157
+ this.layoutRoot.width = this.width;
158
+ this.layoutRoot.height = this.height;
159
+ }
160
+ }
161
+ render(_r) {
162
+ // Handled by children
163
+ }
164
+ }
165
+ //# sourceMappingURL=WorkspaceLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceLayout.js","sourceRoot":"","sources":["../src/WorkspaceLayout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { VemEditorState } from '@vemjs/core';
2
+ export { VemEditorEntity } from './VemEditorEntity';
3
+ export { CommandBar } from './CommandBar';
4
+ export { WorkspaceLayout, EditorPane, type PaneNode } from './WorkspaceLayout';
5
+ export { VemWorkspace } from './Workspace';
6
+ export { WorkspaceExplorer } from './WorkspaceExplorer';
7
+ export declare class VectoRenderer {
8
+ private editorState;
9
+ private scene;
10
+ private editorEntity;
11
+ constructor(editorState: VemEditorState);
12
+ attach(canvas: HTMLCanvasElement): void;
13
+ render(): void;
14
+ }
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,YAAY,CAAgC;gBAExC,WAAW,EAAE,cAAc;IAIhC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAuBvC,MAAM,IAAI,IAAI;CAKtB"}
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import { Scene } from '@vectojs/core';
2
+ import { VemEditorEntity } from './VemEditorEntity';
3
+ export { VemEditorEntity } from './VemEditorEntity';
4
+ export { CommandBar } from './CommandBar';
5
+ export { WorkspaceLayout, EditorPane } from './WorkspaceLayout';
6
+ export { VemWorkspace } from './Workspace';
7
+ export { WorkspaceExplorer } from './WorkspaceExplorer';
8
+ export class VectoRenderer {
9
+ editorState;
10
+ scene = null;
11
+ editorEntity = null;
12
+ constructor(editorState) {
13
+ this.editorState = editorState;
14
+ }
15
+ attach(canvas) {
16
+ this.scene = new Scene(canvas);
17
+ this.editorEntity = new VemEditorEntity(this.editorState);
18
+ this.scene.add(this.editorEntity);
19
+ this.scene.start();
20
+ // Listen to changes in the editor state to update rendering properties
21
+ this.editorState.onChange(() => {
22
+ if (this.editorEntity) {
23
+ this.editorEntity.updateFromState();
24
+ }
25
+ if (this.editorState.getMode() === 'COMMAND') {
26
+ setTimeout(() => {
27
+ const inputEl = this.scene?.getA11yElement('vem-command-input');
28
+ if (inputEl) {
29
+ inputEl.focus();
30
+ }
31
+ }, 10);
32
+ }
33
+ });
34
+ }
35
+ render() {
36
+ if (this.editorEntity) {
37
+ this.editorEntity.updateFromState();
38
+ }
39
+ }
40
+ }
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAiB,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,OAAO,aAAa;IAChB,WAAW,CAAiB;IAC5B,KAAK,GAAiB,IAAI,CAAC;IAC3B,YAAY,GAA2B,IAAI,CAAC;IAEpD,YAAY,WAA2B;QACrC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAEM,MAAM,CAAC,MAAyB;QACrC,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAEnB,uEAAuE;QACvE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE;YAC7B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,CAAC;YACtC,CAAC;YAED,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,SAAS,EAAE,CAAC;gBAC7C,UAAU,CAAC,GAAG,EAAE;oBACd,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,mBAAmB,CAAC,CAAC;oBAChE,IAAI,OAAO,EAAE,CAAC;wBACX,OAAuB,CAAC,KAAK,EAAE,CAAC;oBACnC,CAAC;gBACH,CAAC,EAAE,EAAE,CAAC,CAAC;YACT,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,MAAM;QACX,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@vemjs/renderer-vecto",
3
+ "version": "0.1.0",
4
+ "description": "VectoUI integration renderer for the Vem text editor",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc"
10
+ },
11
+ "dependencies": {
12
+ "@vectojs/core": "0.1.0",
13
+ "@vectojs/ui": "0.1.1",
14
+ "@vemjs/core": "workspace:*"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ }
19
+ }
@@ -0,0 +1,69 @@
1
+ import { UIComponent, Text, Input } from '@vectojs/ui';
2
+ import type { IRenderer } from '@vectojs/core';
3
+ import type { VemEditorState } from '@vemjs/core';
4
+
5
+ export class CommandBar extends UIComponent {
6
+ private editorState: VemEditorState;
7
+ private prefixText: Text;
8
+ private input: Input;
9
+
10
+ constructor(editorState: VemEditorState, width: number) {
11
+ super();
12
+ this.editorState = editorState;
13
+ this.width = width;
14
+ this.height = 30;
15
+
16
+ this.prefixText = new Text(':', {
17
+ font: 'bold 14px monospace',
18
+ color: '#38bdf8', // sky-400
19
+ });
20
+ this.prefixText.setPosition(5, 5);
21
+
22
+ this.input = new Input({
23
+ width: width - 20,
24
+ height: 25,
25
+ font: '14px monospace',
26
+ color: '#e2e8f0',
27
+ bg: '#1e293b', // slate-800
28
+ border: 'transparent',
29
+ onChange: (value) => {
30
+ // Mirror changes to editorState command buffer
31
+ (this.editorState as any).commandText = value;
32
+ },
33
+ });
34
+ this.input.id = 'vem-command-input';
35
+ this.input.setPosition(15, 2);
36
+
37
+ // Watch for Escape and Enter on the input component to bridge to editorState
38
+ this.input.on('keydown', (e: any) => {
39
+ const key = e.nativeEvent?.key || e.key;
40
+ if (key === 'Escape') {
41
+ this.editorState.handleKey('Escape');
42
+ } else if (key === 'Enter') {
43
+ this.editorState.handleKey('Enter');
44
+ }
45
+ });
46
+
47
+ this.add(this.prefixText);
48
+ this.add(this.input);
49
+ }
50
+
51
+ public updateWidth(width: number): void {
52
+ this.width = width;
53
+ this.input.width = width - 20;
54
+ }
55
+
56
+ public clear(): void {
57
+ this.input.value = '';
58
+ }
59
+
60
+ public render(r: IRenderer): void {
61
+ r.beginPath();
62
+ r.moveTo(0, 0);
63
+ r.lineTo(this.width, 0);
64
+ r.lineTo(this.width, this.height);
65
+ r.lineTo(0, this.height);
66
+ r.closePath();
67
+ r.fill('#1e293b'); // slate-800
68
+ }
69
+ }
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect } from 'bun:test';
2
+ import { FileSystemHandler } from './FileSystemHandler';
3
+
4
+ describe('FileSystemHandler', () => {
5
+ it('should map directory entries to TreeNode structures and sort folders first', async () => {
6
+ const mockFileHandle = {
7
+ kind: 'file',
8
+ name: 'index.ts',
9
+ getFile: async () => ({
10
+ text: async () => 'console.log("hello");',
11
+ }),
12
+ };
13
+
14
+ const mockSubDirHandle = {
15
+ kind: 'directory',
16
+ name: 'components',
17
+ values: async function* () {
18
+ yield {
19
+ kind: 'file',
20
+ name: 'Button.ts',
21
+ };
22
+ },
23
+ };
24
+
25
+ const mockDirHandle = {
26
+ kind: 'directory',
27
+ name: 'src',
28
+ values: async function* () {
29
+ yield mockFileHandle;
30
+ yield mockSubDirHandle;
31
+ },
32
+ };
33
+
34
+ const handler = new FileSystemHandler();
35
+ const nodes = await handler.readDirectory(mockDirHandle as any);
36
+
37
+ expect(nodes.length).toBe(2);
38
+ // Folder components should be sorted first
39
+ expect(nodes[0].label).toBe('components');
40
+ expect(nodes[0].icon).toBe('📁');
41
+ expect(nodes[1].label).toBe('index.ts');
42
+ expect(nodes[1].icon).toBe('📄');
43
+
44
+ // Test lazy children loading
45
+ const childrenFunc = nodes[0].children as () => Promise<any[]>;
46
+ expect(typeof childrenFunc).toBe('function');
47
+ const children = await childrenFunc();
48
+ expect(children.length).toBe(1);
49
+ expect(children[0].label).toBe('Button.ts');
50
+ });
51
+ });
@@ -0,0 +1,60 @@
1
+ import type { TreeNode } from '@vectojs/ui';
2
+
3
+ export class FileSystemHandler {
4
+ private fileHandles = new Map<string, FileSystemFileHandle>();
5
+
6
+ public async readDirectory(
7
+ dirHandle: FileSystemDirectoryHandle,
8
+ pathPrefix = '',
9
+ ): Promise<TreeNode[]> {
10
+ const nodes: TreeNode[] = [];
11
+
12
+ for await (const entry of (dirHandle as any).values()) {
13
+ const fullPath = pathPrefix ? `${pathPrefix}/${entry.name}` : entry.name;
14
+
15
+ if (entry.kind === 'directory') {
16
+ nodes.push({
17
+ id: fullPath,
18
+ label: entry.name,
19
+ icon: '📁',
20
+ children: async () => {
21
+ return this.readDirectory(entry, fullPath);
22
+ },
23
+ } as any);
24
+ } else if (entry.kind === 'file') {
25
+ this.fileHandles.set(fullPath, entry);
26
+ nodes.push({
27
+ id: fullPath,
28
+ label: entry.name,
29
+ icon: '📄',
30
+ });
31
+ }
32
+ }
33
+
34
+ // Sort: directories first, then files alphabetically
35
+ nodes.sort((a, b) => {
36
+ const aIsDir = a.icon === '📁';
37
+ const bIsDir = b.icon === '📁';
38
+ if (aIsDir && !bIsDir) return -1;
39
+ if (!aIsDir && bIsDir) return 1;
40
+ return a.label.localeCompare(b.label);
41
+ });
42
+
43
+ return nodes;
44
+ }
45
+
46
+ public getFileHandle(path: string): FileSystemFileHandle | undefined {
47
+ return this.fileHandles.get(path);
48
+ }
49
+
50
+ public async readFile(fileHandle: FileSystemFileHandle): Promise<string> {
51
+ const file = await fileHandle.getFile();
52
+ return await file.text();
53
+ }
54
+
55
+ public async saveFile(fileHandle: FileSystemFileHandle, content: string): Promise<void> {
56
+ const writable = await (fileHandle as any).createWritable();
57
+ await writable.write(content);
58
+ await writable.close();
59
+ }
60
+ }