@things-factory/ai-assistant 10.1.88 → 10.1.91

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 (93) hide show
  1. package/client/components/assistant-chat.ts +45 -5
  2. package/client/components/assistant-dock.ts +30 -2
  3. package/client/components/entry-review.ts +246 -0
  4. package/client/components/workspace-view.ts +428 -0
  5. package/client/pages/assistant-workspace-page.ts +237 -0
  6. package/client/route.ts +9 -0
  7. package/client/utils/assistant-proposals.ts +11 -1
  8. package/client/utils/entry-proposal.ts +103 -0
  9. package/client/utils/workspace.ts +89 -0
  10. package/dist-client/components/assistant-chat.d.ts +3 -0
  11. package/dist-client/components/assistant-chat.js +46 -5
  12. package/dist-client/components/assistant-chat.js.map +1 -1
  13. package/dist-client/components/assistant-dock.d.ts +1 -0
  14. package/dist-client/components/assistant-dock.js +25 -2
  15. package/dist-client/components/assistant-dock.js.map +1 -1
  16. package/dist-client/components/entry-review.d.ts +9 -0
  17. package/dist-client/components/entry-review.js +247 -0
  18. package/dist-client/components/entry-review.js.map +1 -0
  19. package/dist-client/components/workspace-view.d.ts +19 -0
  20. package/dist-client/components/workspace-view.js +416 -0
  21. package/dist-client/components/workspace-view.js.map +1 -0
  22. package/dist-client/pages/assistant-workspace-page.d.ts +27 -0
  23. package/dist-client/pages/assistant-workspace-page.js +263 -0
  24. package/dist-client/pages/assistant-workspace-page.js.map +1 -0
  25. package/dist-client/route.d.ts +1 -0
  26. package/dist-client/route.js +10 -0
  27. package/dist-client/route.js.map +1 -0
  28. package/dist-client/tsconfig.tsbuildinfo +1 -1
  29. package/dist-client/utils/assistant-proposals.d.ts +1 -0
  30. package/dist-client/utils/assistant-proposals.js +9 -1
  31. package/dist-client/utils/assistant-proposals.js.map +1 -1
  32. package/dist-client/utils/entry-proposal.d.ts +55 -0
  33. package/dist-client/utils/entry-proposal.js +52 -0
  34. package/dist-client/utils/entry-proposal.js.map +1 -0
  35. package/dist-client/utils/workspace.d.ts +40 -0
  36. package/dist-client/utils/workspace.js +68 -0
  37. package/dist-client/utils/workspace.js.map +1 -0
  38. package/dist-server/service/assistant-mode/registry.js +13 -0
  39. package/dist-server/service/assistant-mode/registry.js.map +1 -1
  40. package/dist-server/service/assistant-mode/resolve.d.ts +11 -0
  41. package/dist-server/service/assistant-mode/resolve.js +15 -3
  42. package/dist-server/service/assistant-mode/resolve.js.map +1 -1
  43. package/dist-server/service/assistant-mode/types.d.ts +7 -1
  44. package/dist-server/service/assistant-mode/types.js.map +1 -1
  45. package/dist-server/service/data-entry/entry-apply.d.ts +46 -0
  46. package/dist-server/service/data-entry/entry-apply.js +102 -0
  47. package/dist-server/service/data-entry/entry-apply.js.map +1 -0
  48. package/dist-server/service/data-entry/entry-resolver.d.ts +4 -0
  49. package/dist-server/service/data-entry/entry-resolver.js +80 -0
  50. package/dist-server/service/data-entry/entry-resolver.js.map +1 -0
  51. package/dist-server/service/data-entry/entry-sources.d.ts +1 -0
  52. package/dist-server/service/data-entry/entry-sources.js +41 -0
  53. package/dist-server/service/data-entry/entry-sources.js.map +1 -0
  54. package/dist-server/service/data-entry/entry-target.d.ts +157 -0
  55. package/dist-server/service/data-entry/entry-target.js +343 -0
  56. package/dist-server/service/data-entry/entry-target.js.map +1 -0
  57. package/dist-server/service/data-entry/entry-tools.d.ts +43 -0
  58. package/dist-server/service/data-entry/entry-tools.js +185 -0
  59. package/dist-server/service/data-entry/entry-tools.js.map +1 -0
  60. package/dist-server/service/index.d.ts +6 -1
  61. package/dist-server/service/index.js +13 -2
  62. package/dist-server/service/index.js.map +1 -1
  63. package/dist-server/service/workspace/workspace-blocks.d.ts +24 -0
  64. package/dist-server/service/workspace/workspace-blocks.js +118 -0
  65. package/dist-server/service/workspace/workspace-blocks.js.map +1 -0
  66. package/dist-server/service/workspace/workspace-tools.d.ts +24 -0
  67. package/dist-server/service/workspace/workspace-tools.js +65 -0
  68. package/dist-server/service/workspace/workspace-tools.js.map +1 -0
  69. package/dist-server/tsconfig.tsbuildinfo +1 -1
  70. package/package.json +2 -2
  71. package/server/service/assistant-mode/registry.ts +13 -0
  72. package/server/service/assistant-mode/resolve.test.ts +2 -1
  73. package/server/service/assistant-mode/resolve.ts +15 -3
  74. package/server/service/assistant-mode/types.ts +7 -1
  75. package/server/service/data-entry/data-entry.test.ts +311 -0
  76. package/server/service/data-entry/entry-apply.ts +134 -0
  77. package/server/service/data-entry/entry-resolver.ts +74 -0
  78. package/server/service/data-entry/entry-sources.ts +35 -0
  79. package/server/service/data-entry/entry-target.ts +442 -0
  80. package/server/service/data-entry/entry-tools.ts +174 -0
  81. package/server/service/index.ts +10 -1
  82. package/server/service/workspace/workspace-blocks.ts +127 -0
  83. package/server/service/workspace/workspace-tools.ts +66 -0
  84. package/server/service/workspace/workspace.test.ts +58 -0
  85. package/test/entry-proposal.test.ts +67 -0
  86. package/test/open-doors.test.ts +2 -1
  87. package/test/workspace.test.ts +54 -0
  88. package/things-factory.config.js +6 -1
  89. package/translations/en.json +56 -1
  90. package/translations/ja.json +56 -1
  91. package/translations/ko.json +56 -1
  92. package/translations/ms.json +56 -1
  93. package/translations/zh.json +56 -1
@@ -0,0 +1,263 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ /*
3
+ * The AI 작업판 page (operato-application/design/ai/workspace.md).
4
+ *
5
+ * `assistant-workspace?session=<id>&name=<name>[&version=<n>]`. It reads the conversation through its own
6
+ * door (`chatMessages`), so a private conversation shows nothing to someone outside it and a version behind a
7
+ * door the reader cannot pass is not among the versions (ADR-0073). A new version arriving while the page is
8
+ * open is announced, never swapped in under the reader.
9
+ */
10
+ import '@material/web/icon/icon.js';
11
+ import '../components/workspace-view.js';
12
+ import gql from 'graphql-tag';
13
+ import { css, html, nothing } from 'lit';
14
+ import { customElement, state } from 'lit/decorators.js';
15
+ import { client } from '@operato/graphql';
16
+ import { i18next, localize } from '@operato/i18n';
17
+ import { navigate, PageView } from '@operato/shell';
18
+ import { ScrollbarStyles } from '@operato/styles';
19
+ import { startSessionActivityFeed, subscribeSessionActivity } from '../utils/assistant-activity.js';
20
+ import { workspaceAddress, workspaceVersions } from '../utils/workspace.js';
21
+ const MESSAGES = gql `
22
+ query ($sessionId: String!, $limit: Int, $offset: Int) {
23
+ chatMessages(sessionId: $sessionId, limit: $limit, offset: $offset) {
24
+ id
25
+ createdAt
26
+ proposalsJson
27
+ }
28
+ }
29
+ `;
30
+ const PAGE = 200;
31
+ const MOST = 5000;
32
+ const t = (key, defaultValue, options = {}) => String(i18next.t(`ai-assistant.workspace.${key}`, { defaultValue, ...options }));
33
+ let AssistantWorkspacePage = class AssistantWorkspacePage extends localize(i18next)(PageView) {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.sessionId = '';
37
+ this.name = '';
38
+ this.wanted = 0;
39
+ this.versions = [];
40
+ this.loading = false;
41
+ this.failure = '';
42
+ /** A version that arrived after the page was drawn - announced, not shown until asked. */
43
+ this.newer = 0;
44
+ }
45
+ static { this.styles = [
46
+ ScrollbarStyles,
47
+ css `
48
+ :host {
49
+ display: block;
50
+ overflow: auto;
51
+ padding: 20px 24px 40px;
52
+ box-sizing: border-box;
53
+ background: var(--md-sys-color-surface);
54
+ }
55
+
56
+ .page {
57
+ max-width: 1100px;
58
+ margin: 0 auto;
59
+ }
60
+
61
+ header {
62
+ display: flex;
63
+ align-items: flex-start;
64
+ gap: 12px;
65
+ margin-bottom: 16px;
66
+ }
67
+
68
+ header .what {
69
+ flex: 1;
70
+ min-width: 0;
71
+ }
72
+
73
+ header h1 {
74
+ margin: 0;
75
+ font-size: 24px;
76
+ line-height: 1.3;
77
+ }
78
+
79
+ header .summary {
80
+ margin-top: 4px;
81
+ color: var(--md-sys-color-on-surface-variant);
82
+ }
83
+
84
+ header select {
85
+ padding: 4px 8px;
86
+ border: 1px solid var(--md-sys-color-outline-variant);
87
+ border-radius: 6px;
88
+ background: var(--md-sys-color-surface-container-lowest);
89
+ color: inherit;
90
+ font: inherit;
91
+ }
92
+
93
+ .notice {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 10px;
97
+ margin-bottom: 14px;
98
+ padding: 8px 12px;
99
+ border-radius: 8px;
100
+ background: var(--md-sys-color-secondary-container);
101
+ color: var(--md-sys-color-on-secondary-container);
102
+ }
103
+
104
+ .notice button {
105
+ padding: 3px 10px;
106
+ border: 1px solid currentColor;
107
+ border-radius: 6px;
108
+ background: transparent;
109
+ color: inherit;
110
+ font: inherit;
111
+ cursor: pointer;
112
+ }
113
+
114
+ .empty {
115
+ padding: 40px 0;
116
+ color: var(--md-sys-color-on-surface-variant);
117
+ text-align: center;
118
+ }
119
+ `
120
+ ]; }
121
+ get context() {
122
+ const shown = this.shown;
123
+ return { title: shown?.workspace.title || t('title', 'AI workspace') };
124
+ }
125
+ get shown() {
126
+ if (!this.versions.length)
127
+ return undefined;
128
+ return this.versions.find(one => one.version === this.wanted) ?? this.versions[this.versions.length - 1];
129
+ }
130
+ connectedCallback() {
131
+ super.connectedCallback();
132
+ startSessionActivityFeed();
133
+ this.unsubscribe = subscribeSessionActivity(activity => {
134
+ if (activity.sessionId === this.sessionId && activity.kind === 'message')
135
+ void this.load(true);
136
+ });
137
+ }
138
+ disconnectedCallback() {
139
+ this.unsubscribe?.();
140
+ super.disconnectedCallback();
141
+ }
142
+ async pageUpdated(_changes, lifecycle) {
143
+ const params = lifecycle?.params ?? {};
144
+ const sessionId = String(params.session || '');
145
+ const name = String(params.name || '');
146
+ const wanted = Number(params.version) || 0;
147
+ const changed = sessionId !== this.sessionId || name !== this.name;
148
+ this.sessionId = sessionId;
149
+ this.name = name;
150
+ this.wanted = wanted;
151
+ if (changed) {
152
+ this.newer = 0;
153
+ await this.load(false);
154
+ }
155
+ }
156
+ async load(arriving) {
157
+ if (!this.sessionId || !this.name)
158
+ return;
159
+ const before = this.versions.length;
160
+ if (!arriving)
161
+ this.loading = true;
162
+ this.failure = '';
163
+ try {
164
+ const messages = [];
165
+ for (let offset = 0; offset < MOST; offset += PAGE) {
166
+ const result = await client.query({ query: MESSAGES, variables: { sessionId: this.sessionId, limit: PAGE, offset }, fetchPolicy: 'no-cache' });
167
+ if (result.errors?.length)
168
+ throw new Error(result.errors[0].message);
169
+ const page = result.data?.chatMessages ?? [];
170
+ messages.push(...page);
171
+ if (page.length < PAGE)
172
+ break;
173
+ }
174
+ const versions = workspaceVersions(messages, this.name);
175
+ this.versions = versions;
176
+ if (arriving && versions.length > before && before > 0)
177
+ this.newer = versions[versions.length - 1].version;
178
+ }
179
+ catch (error) {
180
+ this.failure = error?.message || String(error);
181
+ }
182
+ finally {
183
+ this.loading = false;
184
+ }
185
+ }
186
+ go(version) {
187
+ this.newer = 0;
188
+ navigate(workspaceAddress(this.sessionId, this.name, version));
189
+ }
190
+ render() {
191
+ const shown = this.shown;
192
+ if (this.loading && !shown)
193
+ return html `<div class="empty">${t('loading', 'loading the workspace…')}</div>`;
194
+ if (this.failure)
195
+ return html `<div class="empty">${t('unreadable-page', 'could not read this workspace: {reason}', { reason: this.failure })}</div>`;
196
+ /*
197
+ * Nothing to show says only what is true: this reader sees no such workspace. Whether it never existed, is in
198
+ * a private conversation or sits behind a door is not said - saying which would itself tell.
199
+ */
200
+ if (!shown)
201
+ return html `<div class="empty">${t('not-found', 'there is no workspace here that you can see.')}</div>`;
202
+ const latest = this.versions[this.versions.length - 1].version;
203
+ return html `<div class="page">
204
+ ${this.newer && this.newer !== shown.version
205
+ ? html `<div class="notice" role="status">
206
+ <md-icon>update</md-icon>
207
+ <span>${t('newer', 'a newer version ({version}) is ready.', { version: this.newer })}</span>
208
+ <button @click=${() => this.go(this.newer)}>${t('show-newer', 'show it')}</button>
209
+ </div>`
210
+ : nothing}
211
+ <header>
212
+ <div class="what">
213
+ <h1>${shown.workspace.title}</h1>
214
+ ${shown.workspace.summary ? html `<div class="summary">${shown.workspace.summary}</div>` : nothing}
215
+ </div>
216
+ ${this.versions.length > 1
217
+ ? html `<select aria-label=${t('versions', 'versions')} @change=${(e) => this.go(Number(e.target.value))}>
218
+ ${this.versions
219
+ .slice()
220
+ .reverse()
221
+ .map(one => html `<option value=${one.version} ?selected=${one.version === shown.version}>
222
+ ${t('version', 'version {version} of {of}', { version: one.version, of: latest })}${one.createdAt ? ` · ${new Date(one.createdAt).toLocaleString(i18next.language || undefined)}` : ''}
223
+ </option>`)}
224
+ </select>`
225
+ : nothing}
226
+ </header>
227
+ <ox-assistant-workspace-view .workspace=${shown.workspace}></ox-assistant-workspace-view>
228
+ </div>`;
229
+ }
230
+ };
231
+ __decorate([
232
+ state(),
233
+ __metadata("design:type", Object)
234
+ ], AssistantWorkspacePage.prototype, "sessionId", void 0);
235
+ __decorate([
236
+ state(),
237
+ __metadata("design:type", Object)
238
+ ], AssistantWorkspacePage.prototype, "name", void 0);
239
+ __decorate([
240
+ state(),
241
+ __metadata("design:type", Object)
242
+ ], AssistantWorkspacePage.prototype, "wanted", void 0);
243
+ __decorate([
244
+ state(),
245
+ __metadata("design:type", Array)
246
+ ], AssistantWorkspacePage.prototype, "versions", void 0);
247
+ __decorate([
248
+ state(),
249
+ __metadata("design:type", Object)
250
+ ], AssistantWorkspacePage.prototype, "loading", void 0);
251
+ __decorate([
252
+ state(),
253
+ __metadata("design:type", Object)
254
+ ], AssistantWorkspacePage.prototype, "failure", void 0);
255
+ __decorate([
256
+ state(),
257
+ __metadata("design:type", Object)
258
+ ], AssistantWorkspacePage.prototype, "newer", void 0);
259
+ AssistantWorkspacePage = __decorate([
260
+ customElement('assistant-workspace-page')
261
+ ], AssistantWorkspacePage);
262
+ export { AssistantWorkspacePage };
263
+ //# sourceMappingURL=assistant-workspace-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistant-workspace-page.js","sourceRoot":"","sources":["../../client/pages/assistant-workspace-page.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The AI 작업판 page (operato-application/design/ai/workspace.md).\n *\n * `assistant-workspace?session=<id>&name=<name>[&version=<n>]`. It reads the conversation through its own\n * door (`chatMessages`), so a private conversation shows nothing to someone outside it and a version behind a\n * door the reader cannot pass is not among the versions (ADR-0073). A new version arriving while the page is\n * open is announced, never swapped in under the reader.\n */\nimport '@material/web/icon/icon.js'\nimport '../components/workspace-view.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { startSessionActivityFeed, subscribeSessionActivity } from '../utils/assistant-activity.js'\nimport { workspaceAddress, workspaceVersions, type WorkspaceVersion } from '../utils/workspace.js'\n\nconst MESSAGES = gql`\n query ($sessionId: String!, $limit: Int, $offset: Int) {\n chatMessages(sessionId: $sessionId, limit: $limit, offset: $offset) {\n id\n createdAt\n proposalsJson\n }\n }\n`\n\nconst PAGE = 200\nconst MOST = 5000\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n String(i18next.t(`ai-assistant.workspace.${key}`, { defaultValue, ...options }))\n\n@customElement('assistant-workspace-page')\nexport class AssistantWorkspacePage extends localize(i18next)(PageView) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n overflow: auto;\n padding: 20px 24px 40px;\n box-sizing: border-box;\n background: var(--md-sys-color-surface);\n }\n\n .page {\n max-width: 1100px;\n margin: 0 auto;\n }\n\n header {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 16px;\n }\n\n header .what {\n flex: 1;\n min-width: 0;\n }\n\n header h1 {\n margin: 0;\n font-size: 24px;\n line-height: 1.3;\n }\n\n header .summary {\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n header select {\n padding: 4px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n }\n\n .notice {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 14px;\n padding: 8px 12px;\n border-radius: 8px;\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n .notice button {\n padding: 3px 10px;\n border: 1px solid currentColor;\n border-radius: 6px;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .empty {\n padding: 40px 0;\n color: var(--md-sys-color-on-surface-variant);\n text-align: center;\n }\n `\n ]\n\n @state() private sessionId = ''\n @state() private name = ''\n @state() private wanted = 0\n @state() private versions: WorkspaceVersion[] = []\n @state() private loading = false\n @state() private failure = ''\n /** A version that arrived after the page was drawn - announced, not shown until asked. */\n @state() private newer = 0\n\n private unsubscribe?: () => void\n\n get context() {\n const shown = this.shown\n return { title: shown?.workspace.title || t('title', 'AI workspace') }\n }\n\n private get shown(): WorkspaceVersion | undefined {\n if (!this.versions.length) return undefined\n return this.versions.find(one => one.version === this.wanted) ?? this.versions[this.versions.length - 1]\n }\n\n connectedCallback() {\n super.connectedCallback()\n startSessionActivityFeed()\n this.unsubscribe = subscribeSessionActivity(activity => {\n if (activity.sessionId === this.sessionId && activity.kind === 'message') void this.load(true)\n })\n }\n\n disconnectedCallback() {\n this.unsubscribe?.()\n super.disconnectedCallback()\n }\n\n async pageUpdated(_changes: any, lifecycle: any) {\n const params = lifecycle?.params ?? {}\n const sessionId = String(params.session || '')\n const name = String(params.name || '')\n const wanted = Number(params.version) || 0\n const changed = sessionId !== this.sessionId || name !== this.name\n this.sessionId = sessionId\n this.name = name\n this.wanted = wanted\n if (changed) {\n this.newer = 0\n await this.load(false)\n }\n }\n\n private async load(arriving: boolean) {\n if (!this.sessionId || !this.name) return\n const before = this.versions.length\n if (!arriving) this.loading = true\n this.failure = ''\n try {\n const messages: any[] = []\n for (let offset = 0; offset < MOST; offset += PAGE) {\n const result = await client.query({ query: MESSAGES, variables: { sessionId: this.sessionId, limit: PAGE, offset }, fetchPolicy: 'no-cache' })\n if (result.errors?.length) throw new Error(result.errors[0].message)\n const page = result.data?.chatMessages ?? []\n messages.push(...page)\n if (page.length < PAGE) break\n }\n const versions = workspaceVersions(messages, this.name)\n this.versions = versions\n if (arriving && versions.length > before && before > 0) this.newer = versions[versions.length - 1].version\n } catch (error: any) {\n this.failure = error?.message || String(error)\n } finally {\n this.loading = false\n }\n }\n\n private go(version?: number) {\n this.newer = 0\n navigate(workspaceAddress(this.sessionId, this.name, version))\n }\n\n render() {\n const shown = this.shown\n if (this.loading && !shown) return html`<div class=\"empty\">${t('loading', 'loading the workspace…')}</div>`\n if (this.failure) return html`<div class=\"empty\">${t('unreadable-page', 'could not read this workspace: {reason}', { reason: this.failure })}</div>`\n /*\n * Nothing to show says only what is true: this reader sees no such workspace. Whether it never existed, is in\n * a private conversation or sits behind a door is not said - saying which would itself tell.\n */\n if (!shown) return html`<div class=\"empty\">${t('not-found', 'there is no workspace here that you can see.')}</div>`\n\n const latest = this.versions[this.versions.length - 1].version\n return html`<div class=\"page\">\n ${this.newer && this.newer !== shown.version\n ? html`<div class=\"notice\" role=\"status\">\n <md-icon>update</md-icon>\n <span>${t('newer', 'a newer version ({version}) is ready.', { version: this.newer })}</span>\n <button @click=${() => this.go(this.newer)}>${t('show-newer', 'show it')}</button>\n </div>`\n : nothing}\n <header>\n <div class=\"what\">\n <h1>${shown.workspace.title}</h1>\n ${shown.workspace.summary ? html`<div class=\"summary\">${shown.workspace.summary}</div>` : nothing}\n </div>\n ${this.versions.length > 1\n ? html`<select aria-label=${t('versions', 'versions')} @change=${(e: Event) => this.go(Number((e.target as HTMLSelectElement).value))}>\n ${this.versions\n .slice()\n .reverse()\n .map(\n one => html`<option value=${one.version} ?selected=${one.version === shown.version}>\n ${t('version', 'version {version} of {of}', { version: one.version, of: latest })}${one.createdAt ? ` · ${new Date(one.createdAt).toLocaleString(i18next.language || undefined)}` : ''}\n </option>`\n )}\n </select>`\n : nothing}\n </header>\n <ox-assistant-workspace-view .workspace=${shown.workspace}></ox-assistant-workspace-view>\n </div>`\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export default function route(page: string): "assistant-workspace" | undefined;
@@ -0,0 +1,10 @@
1
+ /* The pages ai-assistant brings to every application that installs it. */
2
+ export default function route(page) {
3
+ switch (page) {
4
+ /* The AI 작업판 - results too large for the dock (design/ai/workspace.md). */
5
+ case 'assistant-workspace':
6
+ import('./pages/assistant-workspace-page.js');
7
+ return page;
8
+ }
9
+ }
10
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACb,2EAA2E;QAC3E,KAAK,qBAAqB;YACxB,MAAM,CAAC,qCAAqC,CAAC,CAAA;YAC7C,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["/* The pages ai-assistant brings to every application that installs it. */\nexport default function route(page: string) {\n switch (page) {\n /* The AI 작업판 - results too large for the dock (design/ai/workspace.md). */\n case 'assistant-workspace':\n import('./pages/assistant-workspace-page.js')\n return page\n }\n}\n"]}