@things-factory/board-ai 10.0.0-beta.108
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/PLAN-styling-tools.md +1253 -0
- package/client/components/board-ai-chat.test.ts +149 -0
- package/client/components/board-ai-chat.ts +2574 -0
- package/client/components/chat-echo-dedup.test.ts +116 -0
- package/client/components/chat-echo-dedup.ts +89 -0
- package/client/components/chat-input-builder.ts +47 -0
- package/client/components/default-catalog-entries.test.ts +59 -0
- package/client/components/default-catalog-entries.ts +100 -0
- package/client/components/default-slash-templates.test.ts +55 -0
- package/client/components/default-slash-templates.ts +129 -0
- package/client/components/markdown.test.ts +518 -0
- package/client/components/markdown.ts +508 -0
- package/client/components/mention-popup-helpers.ts +433 -0
- package/client/components/mention-popup.test.ts +588 -0
- package/client/components/mention-popup.ts +238 -0
- package/client/index.ts +11 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/board-edit-patch.ts +374 -0
- package/config/config.development.js +43 -0
- package/config/config.production.js +15 -0
- package/dist-client/client/components/board-ai-chat.d.ts +277 -0
- package/dist-client/client/components/board-ai-chat.js +2444 -0
- package/dist-client/client/components/board-ai-chat.js.map +1 -0
- package/dist-client/client/components/board-ai-chat.test.d.ts +1 -0
- package/dist-client/client/components/board-ai-chat.test.js +138 -0
- package/dist-client/client/components/board-ai-chat.test.js.map +1 -0
- package/dist-client/client/components/chat-echo-dedup.d.ts +45 -0
- package/dist-client/client/components/chat-echo-dedup.js +58 -0
- package/dist-client/client/components/chat-echo-dedup.js.map +1 -0
- package/dist-client/client/components/chat-echo-dedup.test.d.ts +1 -0
- package/dist-client/client/components/chat-echo-dedup.test.js +101 -0
- package/dist-client/client/components/chat-echo-dedup.test.js.map +1 -0
- package/dist-client/client/components/chat-input-builder.d.ts +39 -0
- package/dist-client/client/components/chat-input-builder.js +26 -0
- package/dist-client/client/components/chat-input-builder.js.map +1 -0
- package/dist-client/client/components/default-catalog-entries.d.ts +15 -0
- package/dist-client/client/components/default-catalog-entries.js +86 -0
- package/dist-client/client/components/default-catalog-entries.js.map +1 -0
- package/dist-client/client/components/default-catalog-entries.test.d.ts +1 -0
- package/dist-client/client/components/default-catalog-entries.test.js +52 -0
- package/dist-client/client/components/default-catalog-entries.test.js.map +1 -0
- package/dist-client/client/components/default-slash-templates.d.ts +17 -0
- package/dist-client/client/components/default-slash-templates.js +113 -0
- package/dist-client/client/components/default-slash-templates.js.map +1 -0
- package/dist-client/client/components/default-slash-templates.test.d.ts +1 -0
- package/dist-client/client/components/default-slash-templates.test.js +51 -0
- package/dist-client/client/components/default-slash-templates.test.js.map +1 -0
- package/dist-client/client/components/markdown.d.ts +60 -0
- package/dist-client/client/components/markdown.js +422 -0
- package/dist-client/client/components/markdown.js.map +1 -0
- package/dist-client/client/components/markdown.test.d.ts +1 -0
- package/dist-client/client/components/markdown.test.js +418 -0
- package/dist-client/client/components/markdown.test.js.map +1 -0
- package/dist-client/client/components/mention-popup-helpers.d.ts +165 -0
- package/dist-client/client/components/mention-popup-helpers.js +330 -0
- package/dist-client/client/components/mention-popup-helpers.js.map +1 -0
- package/dist-client/client/components/mention-popup.d.ts +35 -0
- package/dist-client/client/components/mention-popup.js +223 -0
- package/dist-client/client/components/mention-popup.js.map +1 -0
- package/dist-client/client/components/mention-popup.test.d.ts +1 -0
- package/dist-client/client/components/mention-popup.test.js +474 -0
- package/dist-client/client/components/mention-popup.test.js.map +1 -0
- package/dist-client/client/index.d.ts +11 -0
- package/dist-client/client/index.js +12 -0
- package/dist-client/client/index.js.map +1 -0
- package/dist-client/client/utils/board-edit-patch.d.ts +157 -0
- package/dist-client/client/utils/board-edit-patch.js +284 -0
- package/dist-client/client/utils/board-edit-patch.js.map +1 -0
- package/dist-client/server/service/agentic-loop.d.ts +87 -0
- package/dist-client/server/service/agentic-loop.js +276 -0
- package/dist-client/server/service/agentic-loop.js.map +1 -0
- package/dist-client/server/service/assistant.d.ts +84 -0
- package/dist-client/server/service/assistant.js +2220 -0
- package/dist-client/server/service/assistant.js.map +1 -0
- package/dist-client/server/service/catalog-resolver.d.ts +18 -0
- package/dist-client/server/service/catalog-resolver.js +93 -0
- package/dist-client/server/service/catalog-resolver.js.map +1 -0
- package/dist-client/server/service/component-tree.d.ts +68 -0
- package/dist-client/server/service/component-tree.js +68 -0
- package/dist-client/server/service/component-tree.js.map +1 -0
- package/dist-client/server/service/mention-resolver.d.ts +96 -0
- package/dist-client/server/service/mention-resolver.js +172 -0
- package/dist-client/server/service/mention-resolver.js.map +1 -0
- package/dist-client/server/service/prefab-registry.d.ts +73 -0
- package/dist-client/server/service/prefab-registry.js +252 -0
- package/dist-client/server/service/prefab-registry.js.map +1 -0
- package/dist-client/server/service/styling/clear-tools.d.ts +15 -0
- package/dist-client/server/service/styling/clear-tools.js +50 -0
- package/dist-client/server/service/styling/clear-tools.js.map +1 -0
- package/dist-client/server/service/styling/color-utils.d.ts +43 -0
- package/dist-client/server/service/styling/color-utils.js +205 -0
- package/dist-client/server/service/styling/color-utils.js.map +1 -0
- package/dist-client/server/service/styling/copy-tools.d.ts +27 -0
- package/dist-client/server/service/styling/copy-tools.js +78 -0
- package/dist-client/server/service/styling/copy-tools.js.map +1 -0
- package/dist-client/server/service/styling/effect-tools.d.ts +45 -0
- package/dist-client/server/service/styling/effect-tools.js +85 -0
- package/dist-client/server/service/styling/effect-tools.js.map +1 -0
- package/dist-client/server/service/styling/fill-tools.d.ts +60 -0
- package/dist-client/server/service/styling/fill-tools.js +226 -0
- package/dist-client/server/service/styling/fill-tools.js.map +1 -0
- package/dist-client/server/service/styling/material-tools.d.ts +32 -0
- package/dist-client/server/service/styling/material-tools.js +88 -0
- package/dist-client/server/service/styling/material-tools.js.map +1 -0
- package/dist-client/server/service/styling/read-tools.d.ts +34 -0
- package/dist-client/server/service/styling/read-tools.js +42 -0
- package/dist-client/server/service/styling/read-tools.js.map +1 -0
- package/dist-client/server/service/styling/registry.d.ts +91 -0
- package/dist-client/server/service/styling/registry.js +345 -0
- package/dist-client/server/service/styling/registry.js.map +1 -0
- package/dist-client/server/service/styling/stroke-tools.d.ts +35 -0
- package/dist-client/server/service/styling/stroke-tools.js +94 -0
- package/dist-client/server/service/styling/stroke-tools.js.map +1 -0
- package/dist-client/server/service/styling/text-tools.d.ts +32 -0
- package/dist-client/server/service/styling/text-tools.js +101 -0
- package/dist-client/server/service/styling/text-tools.js.map +1 -0
- package/dist-client/server/service/types.d.ts +265 -0
- package/dist-client/server/service/types.js +2 -0
- package/dist-client/server/service/types.js.map +1 -0
- package/dist-client/server/service/validation/board-model-schema.d.ts +8311 -0
- package/dist-client/server/service/validation/board-model-schema.js +402 -0
- package/dist-client/server/service/validation/board-model-schema.js.map +1 -0
- package/dist-client/server/service/validation/tool-validation.d.ts +31 -0
- package/dist-client/server/service/validation/tool-validation.js +193 -0
- package/dist-client/server/service/validation/tool-validation.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/index.d.ts +23 -0
- package/dist-server/index.js +27 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/agentic-loop.d.ts +87 -0
- package/dist-server/service/agentic-loop.js +280 -0
- package/dist-server/service/agentic-loop.js.map +1 -0
- package/dist-server/service/apply-patch.d.ts +47 -0
- package/dist-server/service/apply-patch.js +369 -0
- package/dist-server/service/apply-patch.js.map +1 -0
- package/dist-server/service/assistant.d.ts +84 -0
- package/dist-server/service/assistant.js +2237 -0
- package/dist-server/service/assistant.js.map +1 -0
- package/dist-server/service/board-ai-resolver.d.ts +54 -0
- package/dist-server/service/board-ai-resolver.js +409 -0
- package/dist-server/service/board-ai-resolver.js.map +1 -0
- package/dist-server/service/catalog-resolver.d.ts +18 -0
- package/dist-server/service/catalog-resolver.js +98 -0
- package/dist-server/service/catalog-resolver.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-broadcast.d.ts +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-publish.d.ts +6 -0
- package/dist-server/service/chat-message/chat-message-publish.js +27 -0
- package/dist-server/service/chat-message/chat-message-publish.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-subscription.d.ts +8 -0
- package/dist-server/service/chat-message/chat-message-subscription.js +53 -0
- package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-message.d.ts +56 -0
- package/dist-server/service/chat-message/chat-message.js +149 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -0
- package/dist-server/service/chat-message/index.d.ts +5 -0
- package/dist-server/service/chat-message/index.js +12 -0
- package/dist-server/service/chat-message/index.js.map +1 -0
- package/dist-server/service/chat-session/chat-session.d.ts +31 -0
- package/dist-server/service/chat-session/chat-session.js +131 -0
- package/dist-server/service/chat-session/chat-session.js.map +1 -0
- package/dist-server/service/chat-session/index.d.ts +3 -0
- package/dist-server/service/chat-session/index.js +7 -0
- package/dist-server/service/chat-session/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +30 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js +112 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -0
- package/dist-server/service/chat-session-participant/index.d.ts +4 -0
- package/dist-server/service/chat-session-participant/index.js +10 -0
- package/dist-server/service/chat-session-participant/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +27 -0
- package/dist-server/service/chat-session-participant/invite-notification.js +30 -0
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -0
- package/dist-server/service/chat-session-participant/presence.d.ts +4 -0
- package/dist-server/service/chat-session-participant/presence.js +22 -0
- package/dist-server/service/chat-session-participant/presence.js.map +1 -0
- package/dist-server/service/chat-session-resolver.d.ts +20 -0
- package/dist-server/service/chat-session-resolver.js +356 -0
- package/dist-server/service/chat-session-resolver.js.map +1 -0
- package/dist-server/service/component-tree.d.ts +68 -0
- package/dist-server/service/component-tree.js +73 -0
- package/dist-server/service/component-tree.js.map +1 -0
- package/dist-server/service/index.d.ts +20 -0
- package/dist-server/service/index.js +34 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/mention-marker.d.ts +78 -0
- package/dist-server/service/mention-marker.js +162 -0
- package/dist-server/service/mention-marker.js.map +1 -0
- package/dist-server/service/mention-resolver.d.ts +96 -0
- package/dist-server/service/mention-resolver.js +177 -0
- package/dist-server/service/mention-resolver.js.map +1 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.d.ts +13 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.js +13 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.js.map +1 -0
- package/dist-server/service/patch-entry/board-patch-subscription.d.ts +12 -0
- package/dist-server/service/patch-entry/board-patch-subscription.js +63 -0
- package/dist-server/service/patch-entry/board-patch-subscription.js.map +1 -0
- package/dist-server/service/patch-entry/index.d.ts +4 -0
- package/dist-server/service/patch-entry/index.js +12 -0
- package/dist-server/service/patch-entry/index.js.map +1 -0
- package/dist-server/service/patch-entry/patch-entry.d.ts +16 -0
- package/dist-server/service/patch-entry/patch-entry.js +96 -0
- package/dist-server/service/patch-entry/patch-entry.js.map +1 -0
- package/dist-server/service/prefab-registry.d.ts +73 -0
- package/dist-server/service/prefab-registry.js +258 -0
- package/dist-server/service/prefab-registry.js.map +1 -0
- package/dist-server/service/styling/clear-tools.d.ts +15 -0
- package/dist-server/service/styling/clear-tools.js +53 -0
- package/dist-server/service/styling/clear-tools.js.map +1 -0
- package/dist-server/service/styling/color-utils.d.ts +43 -0
- package/dist-server/service/styling/color-utils.js +210 -0
- package/dist-server/service/styling/color-utils.js.map +1 -0
- package/dist-server/service/styling/copy-tools.d.ts +27 -0
- package/dist-server/service/styling/copy-tools.js +81 -0
- package/dist-server/service/styling/copy-tools.js.map +1 -0
- package/dist-server/service/styling/effect-tools.d.ts +45 -0
- package/dist-server/service/styling/effect-tools.js +90 -0
- package/dist-server/service/styling/effect-tools.js.map +1 -0
- package/dist-server/service/styling/fill-tools.d.ts +60 -0
- package/dist-server/service/styling/fill-tools.js +229 -0
- package/dist-server/service/styling/fill-tools.js.map +1 -0
- package/dist-server/service/styling/material-tools.d.ts +32 -0
- package/dist-server/service/styling/material-tools.js +92 -0
- package/dist-server/service/styling/material-tools.js.map +1 -0
- package/dist-server/service/styling/read-tools.d.ts +34 -0
- package/dist-server/service/styling/read-tools.js +45 -0
- package/dist-server/service/styling/read-tools.js.map +1 -0
- package/dist-server/service/styling/registry.d.ts +91 -0
- package/dist-server/service/styling/registry.js +352 -0
- package/dist-server/service/styling/registry.js.map +1 -0
- package/dist-server/service/styling/stroke-tools.d.ts +35 -0
- package/dist-server/service/styling/stroke-tools.js +97 -0
- package/dist-server/service/styling/stroke-tools.js.map +1 -0
- package/dist-server/service/styling/text-tools.d.ts +32 -0
- package/dist-server/service/styling/text-tools.js +105 -0
- package/dist-server/service/styling/text-tools.js.map +1 -0
- package/dist-server/service/subscribe-auth.d.ts +14 -0
- package/dist-server/service/subscribe-auth.js +33 -0
- package/dist-server/service/subscribe-auth.js.map +1 -0
- package/dist-server/service/types.d.ts +265 -0
- package/dist-server/service/types.js +3 -0
- package/dist-server/service/types.js.map +1 -0
- package/dist-server/service/validation/board-model-schema.d.ts +8311 -0
- package/dist-server/service/validation/board-model-schema.js +409 -0
- package/dist-server/service/validation/board-model-schema.js.map +1 -0
- package/dist-server/service/validation/tool-validation.d.ts +31 -0
- package/dist-server/service/validation/tool-validation.js +196 -0
- package/dist-server/service/validation/tool-validation.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +48 -0
- package/server/index.ts +23 -0
- package/server/service/agentic-loop.test.ts +705 -0
- package/server/service/agentic-loop.ts +431 -0
- package/server/service/apply-patch-drift.test.ts +829 -0
- package/server/service/apply-patch.test.ts +757 -0
- package/server/service/apply-patch.ts +411 -0
- package/server/service/assistant-integration.test.ts +922 -0
- package/server/service/assistant-llm-smoke.test.ts +296 -0
- package/server/service/assistant.test.ts +2034 -0
- package/server/service/assistant.ts +2378 -0
- package/server/service/board-ai-resolver.ts +407 -0
- package/server/service/catalog-resolver.test.ts +137 -0
- package/server/service/catalog-resolver.ts +102 -0
- package/server/service/chat-message/chat-message-broadcast.integration.test.ts +73 -0
- package/server/service/chat-message/chat-message-broadcast.test.ts +40 -0
- package/server/service/chat-message/chat-message-broadcast.ts +27 -0
- package/server/service/chat-message/chat-message-publish.ts +22 -0
- package/server/service/chat-message/chat-message-subscription.ts +41 -0
- package/server/service/chat-message/chat-message.ts +169 -0
- package/server/service/chat-message/index.ts +12 -0
- package/server/service/chat-session/chat-session.ts +130 -0
- package/server/service/chat-session/index.ts +5 -0
- package/server/service/chat-session-participant/chat-session-participant.ts +110 -0
- package/server/service/chat-session-participant/index.ts +6 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +30 -0
- package/server/service/chat-session-participant/invite-notification.ts +48 -0
- package/server/service/chat-session-participant/presence.test.ts +39 -0
- package/server/service/chat-session-participant/presence.ts +21 -0
- package/server/service/chat-session-resolver.ts +312 -0
- package/server/service/component-tree.test.ts +248 -0
- package/server/service/component-tree.ts +135 -0
- package/server/service/index.ts +32 -0
- package/server/service/mention-marker.extract.test.ts +61 -0
- package/server/service/mention-marker.test.ts +228 -0
- package/server/service/mention-marker.ts +184 -0
- package/server/service/mention-resolver.test.ts +273 -0
- package/server/service/mention-resolver.ts +264 -0
- package/server/service/patch-entry/board-patch-broadcast.ts +24 -0
- package/server/service/patch-entry/board-patch-subscription.test.ts +40 -0
- package/server/service/patch-entry/board-patch-subscription.ts +50 -0
- package/server/service/patch-entry/index.ts +12 -0
- package/server/service/patch-entry/patch-entry.ts +89 -0
- package/server/service/prefab-registry.test.ts +172 -0
- package/server/service/prefab-registry.ts +332 -0
- package/server/service/styling/clear-tools.test.ts +164 -0
- package/server/service/styling/clear-tools.ts +69 -0
- package/server/service/styling/color-utils.test.ts +130 -0
- package/server/service/styling/color-utils.ts +226 -0
- package/server/service/styling/copy-tools.test.ts +152 -0
- package/server/service/styling/copy-tools.ts +115 -0
- package/server/service/styling/effect-tools.test.ts +277 -0
- package/server/service/styling/effect-tools.ts +124 -0
- package/server/service/styling/fill-tools.test.ts +331 -0
- package/server/service/styling/fill-tools.ts +293 -0
- package/server/service/styling/material-tools.test.ts +294 -0
- package/server/service/styling/material-tools.ts +114 -0
- package/server/service/styling/read-tools.test.ts +78 -0
- package/server/service/styling/read-tools.ts +67 -0
- package/server/service/styling/registry.test.ts +203 -0
- package/server/service/styling/registry.ts +423 -0
- package/server/service/styling/stroke-tools.test.ts +245 -0
- package/server/service/styling/stroke-tools.ts +145 -0
- package/server/service/styling/text-tools.test.ts +253 -0
- package/server/service/styling/text-tools.ts +131 -0
- package/server/service/styling/tier1-tools.test.ts +300 -0
- package/server/service/subscribe-auth.test.ts +57 -0
- package/server/service/subscribe-auth.ts +34 -0
- package/server/service/types.ts +235 -0
- package/server/service/validation/board-model-schema.test.ts +778 -0
- package/server/service/validation/board-model-schema.ts +446 -0
- package/server/service/validation/tool-validation.ts +228 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +49 -0
- package/translations/ja.json +48 -0
- package/translations/ko.json +49 -0
- package/translations/ms.json +48 -0
- package/translations/zh.json +48 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { receiveRemoteMessage, reconcileSentLine, remoteMessageToLine, type DedupLine } from './chat-echo-dedup'
|
|
2
|
+
|
|
3
|
+
const msg = (id: string, role = 'user', content = 'hi') => ({ id, role, content })
|
|
4
|
+
|
|
5
|
+
describe('remoteMessageToLine', () => {
|
|
6
|
+
it('필드 매핑 + relatedPatchId/toolUsagesJson 변환', () => {
|
|
7
|
+
const line = remoteMessageToLine({
|
|
8
|
+
id: 'm1',
|
|
9
|
+
role: 'assistant',
|
|
10
|
+
content: 'done',
|
|
11
|
+
relatedPatchId: 'p1',
|
|
12
|
+
toolUsagesJson: [{ name: 'x' }]
|
|
13
|
+
})
|
|
14
|
+
expect(line).toMatchObject({ id: 'm1', role: 'assistant', content: 'done', patchId: 'p1' })
|
|
15
|
+
expect(line.toolUsages).toEqual([{ name: 'x' }])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('toolUsagesJson 이 배열 아니면 undefined', () => {
|
|
19
|
+
expect(remoteMessageToLine({ id: 'm', role: 'user', content: 'x', toolUsagesJson: null }).toolUsages).toBeUndefined()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('creator → senderId/senderName (그룹챗 발신자 표시)', () => {
|
|
23
|
+
const line = remoteMessageToLine({ id: 'm', role: 'user', content: 'x', creator: { id: 'u9', name: 'Kim' } })
|
|
24
|
+
expect(line.senderId).toBe('u9')
|
|
25
|
+
expect(line.senderName).toBe('Kim')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('creator 없으면 sender 필드 undefined', () => {
|
|
29
|
+
const line = remoteMessageToLine({ id: 'm', role: 'user', content: 'x', creator: null })
|
|
30
|
+
expect(line.senderId).toBeUndefined()
|
|
31
|
+
expect(line.senderName).toBeUndefined()
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('receiveRemoteMessage — 방송 수신/에코 차단', () => {
|
|
36
|
+
it('신규 id → append + seen 등록', () => {
|
|
37
|
+
const res = receiveRemoteMessage([], new Set(), msg('m1'))!
|
|
38
|
+
expect(res).not.toBeNull()
|
|
39
|
+
expect(res.lines).toHaveLength(1)
|
|
40
|
+
expect(res.lines[0].id).toBe('m1')
|
|
41
|
+
expect(res.seen.has('m1')).toBe(true)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('이미 seen 인 id → null (에코·중복 차단)', () => {
|
|
45
|
+
expect(receiveRemoteMessage([], new Set(['m1']), msg('m1'))).toBeNull()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('id 없는 메시지 → null', () => {
|
|
49
|
+
expect(receiveRemoteMessage([], new Set(), { role: 'user', content: 'x' } as any)).toBeNull()
|
|
50
|
+
expect(receiveRemoteMessage([], new Set(), null)).toBeNull()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('불변 — 원본 배열/셋 미변경', () => {
|
|
54
|
+
const lines: DedupLine[] = []
|
|
55
|
+
const seen = new Set<string>()
|
|
56
|
+
receiveRemoteMessage(lines, seen, msg('m1'))
|
|
57
|
+
expect(lines).toHaveLength(0)
|
|
58
|
+
expect(seen.size).toBe(0)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('다른 참여자 메시지 두 개 순차 append', () => {
|
|
62
|
+
let lines: DedupLine[] = []
|
|
63
|
+
let seen = new Set<string>()
|
|
64
|
+
;({ lines, seen } = receiveRemoteMessage(lines, seen, msg('a'))!)
|
|
65
|
+
;({ lines, seen } = receiveRemoteMessage(lines, seen, msg('b'))!)
|
|
66
|
+
expect(lines.map(l => l.id)).toEqual(['a', 'b'])
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('reconcileSentLine — optimistic 라인 조정', () => {
|
|
71
|
+
const optimistic = (): DedupLine[] => [{ _localId: 'L1', role: 'user', content: 'typed' }]
|
|
72
|
+
|
|
73
|
+
it('정상(에코 전): serverId 부여 + seen 등록 + _localId 해제', () => {
|
|
74
|
+
const res = reconcileSentLine(optimistic(), new Set(), 'L1', 'srv1', {})
|
|
75
|
+
expect(res.lines).toHaveLength(1)
|
|
76
|
+
expect(res.lines[0].id).toBe('srv1')
|
|
77
|
+
expect(res.lines[0]._localId).toBeUndefined()
|
|
78
|
+
expect(res.seen.has('srv1')).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('레이스(에코 먼저): serverId 가 이미 seen → optimistic 중복 제거', () => {
|
|
82
|
+
// 에코가 먼저 도착해 같은 메시지가 이미 append + seen 됨
|
|
83
|
+
const lines: DedupLine[] = [
|
|
84
|
+
{ id: 'srv1', role: 'user', content: 'typed#enriched' }, // 방송 에코로 들어온 것
|
|
85
|
+
{ _localId: 'L1', role: 'user', content: 'typed' } // optimistic 중복
|
|
86
|
+
]
|
|
87
|
+
const res = reconcileSentLine(lines, new Set(['srv1']), 'L1', 'srv1', {})
|
|
88
|
+
expect(res.lines).toHaveLength(1)
|
|
89
|
+
expect(res.lines[0].id).toBe('srv1')
|
|
90
|
+
expect(res.lines.some(l => l._localId === 'L1')).toBe(false)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('patch 병합 (AI 라인: pending → 실제 응답)', () => {
|
|
94
|
+
const lines: DedupLine[] = [{ _localId: 'A1', role: 'assistant', content: '', pending: true }]
|
|
95
|
+
const res = reconcileSentLine(lines, new Set(), 'A1', 'srv2', {
|
|
96
|
+
content: 'reply',
|
|
97
|
+
pending: false,
|
|
98
|
+
patchId: 'p9'
|
|
99
|
+
})
|
|
100
|
+
expect(res.lines[0]).toMatchObject({ id: 'srv2', content: 'reply', pending: false, patchId: 'p9' })
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('serverId 없음(ad-hoc 세션): 라인 유지 + _localId 만 해제, seen 불변', () => {
|
|
104
|
+
const seen = new Set<string>()
|
|
105
|
+
const res = reconcileSentLine(optimistic(), seen, 'L1', undefined, {})
|
|
106
|
+
expect(res.lines[0]._localId).toBeUndefined()
|
|
107
|
+
expect(res.lines[0].id).toBeUndefined()
|
|
108
|
+
expect(res.seen.size).toBe(0)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('해당 _localId 없으면 변화 없음', () => {
|
|
112
|
+
const lines = optimistic()
|
|
113
|
+
const res = reconcileSentLine(lines, new Set(), 'NOPE', 'srv1', {})
|
|
114
|
+
expect(res.lines).toBe(lines)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 그룹챗 메시지 에코 dedup — 순수 로직.
|
|
3
|
+
*
|
|
4
|
+
* 실시간 방송은 자기 발신 메시지도 되돌려준다(에코). 또 pubsub publish 가 mutation 응답보다
|
|
5
|
+
* 먼저 도착할 수 있어(레이스) optimistic 라인과 방송 라인이 겹칠 수 있다. 이 모듈은 그 조정을
|
|
6
|
+
* **id 기반**으로 결정한다 (서버가 멘션 마커로 content 를 enrich 하므로 content 매칭은 불가).
|
|
7
|
+
*
|
|
8
|
+
* 컴포넌트 상태(this.lines / this._seenMessageIds)에서 분리 → 단위테스트 가능.
|
|
9
|
+
* (C2 의 patch-apply-gate.ts 와 동일한 "순수 게이트" 컨벤션.)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** dedup 에 필요한 최소 라인 형태 (board-ai-chat 의 ChatLine 부분집합). */
|
|
13
|
+
export interface DedupLine {
|
|
14
|
+
id?: string
|
|
15
|
+
_localId?: string
|
|
16
|
+
role: string
|
|
17
|
+
content: string
|
|
18
|
+
senderId?: string
|
|
19
|
+
senderName?: string
|
|
20
|
+
[k: string]: any
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 서버 방송 메시지(필요 필드만). */
|
|
24
|
+
export interface RemoteMessage {
|
|
25
|
+
id?: string
|
|
26
|
+
role: string
|
|
27
|
+
content: string
|
|
28
|
+
relatedPatchId?: string
|
|
29
|
+
toolUsagesJson?: any
|
|
30
|
+
creator?: { id?: string; name?: string } | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 방송 메시지 → 라인. */
|
|
34
|
+
export function remoteMessageToLine(m: RemoteMessage): DedupLine {
|
|
35
|
+
return {
|
|
36
|
+
id: m.id,
|
|
37
|
+
role: m.role,
|
|
38
|
+
content: m.content,
|
|
39
|
+
patchId: m.relatedPatchId ?? undefined,
|
|
40
|
+
toolUsages: Array.isArray(m.toolUsagesJson) ? m.toolUsagesJson : undefined,
|
|
41
|
+
senderId: m.creator?.id ?? undefined,
|
|
42
|
+
senderName: m.creator?.name ?? undefined
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 원격(방송) 메시지 수신 결과를 계산.
|
|
48
|
+
* - id 없음 / 이미 seen → 변화 없음(null).
|
|
49
|
+
* - 신규 → seen 에 추가 + 라인 append.
|
|
50
|
+
* 반환: 변경된 { lines, seen } (불변 — 새 배열/셋) 또는 null(무시).
|
|
51
|
+
*/
|
|
52
|
+
export function receiveRemoteMessage(
|
|
53
|
+
lines: DedupLine[],
|
|
54
|
+
seen: Set<string>,
|
|
55
|
+
m: RemoteMessage | undefined | null
|
|
56
|
+
): { lines: DedupLine[]; seen: Set<string> } | null {
|
|
57
|
+
if (!m || !m.id) return null
|
|
58
|
+
if (seen.has(m.id)) return null
|
|
59
|
+
const nextSeen = new Set(seen)
|
|
60
|
+
nextSeen.add(m.id)
|
|
61
|
+
return { lines: [...lines, remoteMessageToLine(m)], seen: nextSeen }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 전송한 optimistic 라인(_localId)을 서버 id 로 reconcile.
|
|
66
|
+
* - 에코가 먼저 도착해 serverId 가 이미 seen → optimistic 중복 라인 제거.
|
|
67
|
+
* - 아니면 serverId 부여(+ seen 등록) + patch 병합 + _localId 해제.
|
|
68
|
+
* - 해당 _localId 라인이 없으면 변화 없음.
|
|
69
|
+
* 반환: 새 { lines, seen } (불변).
|
|
70
|
+
*/
|
|
71
|
+
export function reconcileSentLine(
|
|
72
|
+
lines: DedupLine[],
|
|
73
|
+
seen: Set<string>,
|
|
74
|
+
localId: string,
|
|
75
|
+
serverId: string | undefined,
|
|
76
|
+
patch: Partial<DedupLine>
|
|
77
|
+
): { lines: DedupLine[]; seen: Set<string> } {
|
|
78
|
+
const idx = lines.findIndex(l => l._localId === localId)
|
|
79
|
+
if (idx < 0) return { lines, seen }
|
|
80
|
+
|
|
81
|
+
const nextLines = [...lines]
|
|
82
|
+
if (serverId && seen.has(serverId)) {
|
|
83
|
+
nextLines.splice(idx, 1)
|
|
84
|
+
return { lines: nextLines, seen }
|
|
85
|
+
}
|
|
86
|
+
const nextSeen = serverId ? new Set(seen).add(serverId) : seen
|
|
87
|
+
nextLines[idx] = { ...nextLines[idx], ...patch, id: serverId ?? nextLines[idx].id, _localId: undefined }
|
|
88
|
+
return { lines: nextLines, seen: nextSeen }
|
|
89
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boardAIChat GraphQL mutation 의 input 객체 빌더.
|
|
3
|
+
*
|
|
4
|
+
* board-ai-chat.ts (Lit / Material Web 의존) 와 분리 — pure 함수만 두면 jest 의 node
|
|
5
|
+
* 환경에서도 import 가능. 회귀 테스트 (selectedRefids 누락 등) 가 단위 테스트로 잠긴다.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface ChatMutationInputArgs {
|
|
9
|
+
sessionId?: string
|
|
10
|
+
history: Array<{ role: string; content: string }>
|
|
11
|
+
liveBoard: any
|
|
12
|
+
scopes?: string[]
|
|
13
|
+
knownTypes?: string[]
|
|
14
|
+
categories?: string[]
|
|
15
|
+
componentSchemas?: any
|
|
16
|
+
/**
|
|
17
|
+
* 선택된 컴포넌트의 `refid` 배열 — things-scene universal numeric handle.
|
|
18
|
+
* id (데이터 바인딩 이름) 와는 다른 개념.
|
|
19
|
+
*/
|
|
20
|
+
selectedRefids: number[]
|
|
21
|
+
/**
|
|
22
|
+
* popup 에서 명시 선택한 #mention 매핑 — {token, refid}.
|
|
23
|
+
* 사용자 textarea 에는 #token 만 보이고, 정확한 refid 는 이 별도 채널로 server 에 전달.
|
|
24
|
+
* server 가 mention map 으로 LLM 에 정확한 refid 정보 첨부 (사용자 노출 0).
|
|
25
|
+
*/
|
|
26
|
+
mentions?: Array<{ token: string; refid: number }>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* GraphQL mutation 의 input 객체를 빌드.
|
|
31
|
+
*
|
|
32
|
+
* 핵심 보호 — `selectedRefids` 가 항상 포함되어야 한다. 누락이 사용자에게 보고된
|
|
33
|
+
* "AI 가 선택된 컴포넌트를 모른다" 의 root cause 였다.
|
|
34
|
+
*/
|
|
35
|
+
export function buildChatMutationInput(args: ChatMutationInputArgs): Record<string, any> {
|
|
36
|
+
return {
|
|
37
|
+
sessionId: args.sessionId ?? null,
|
|
38
|
+
messages: args.history,
|
|
39
|
+
currentBoard: args.liveBoard ?? null,
|
|
40
|
+
scopes: args.scopes,
|
|
41
|
+
knownTypes: args.knownTypes,
|
|
42
|
+
categories: args.categories,
|
|
43
|
+
componentSchemas: args.componentSchemas,
|
|
44
|
+
selectedRefids: args.selectedRefids,
|
|
45
|
+
...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions })
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* default-catalog-entries.ts 의 client-side hardcoded 카탈로그가 server-side 의
|
|
3
|
+
* prefab-registry / COLOR_PALETTES / LAYOUT_TEMPLATES 와 동기화되어 있는지 검증.
|
|
4
|
+
*
|
|
5
|
+
* 새 default 추가 / 이름 변경 시 server-side 만 바꾸고 client 누락하면 popup 에서
|
|
6
|
+
* 사용자 보지 못해 discoverability 무너짐 — 이 drift 테스트가 즉시 잡음.
|
|
7
|
+
*
|
|
8
|
+
* 향후 GraphQL 'boardAICatalog' query 도입으로 client 가 server 에서 동적 로드하면
|
|
9
|
+
* 이 파일과 default-catalog-entries.ts 모두 제거 가능.
|
|
10
|
+
*/
|
|
11
|
+
import { DEFAULT_CATALOG_ENTRIES } from './default-catalog-entries'
|
|
12
|
+
import { listBoardPrefabs } from '../../server/service/prefab-registry'
|
|
13
|
+
import {
|
|
14
|
+
listColorPaletteNames,
|
|
15
|
+
listLayoutTemplateNames
|
|
16
|
+
} from '../../server/service/assistant'
|
|
17
|
+
|
|
18
|
+
describe('Default catalog entries — server/client drift 검증', () => {
|
|
19
|
+
test('client prefab 이름이 server prefab-registry 와 일치', () => {
|
|
20
|
+
const clientPrefabs = DEFAULT_CATALOG_ENTRIES.filter(e => e.kind === 'prefab')
|
|
21
|
+
.map(e => e.name)
|
|
22
|
+
.sort()
|
|
23
|
+
const serverPrefabs = listBoardPrefabs()
|
|
24
|
+
.map(p => p.name)
|
|
25
|
+
.sort()
|
|
26
|
+
expect(clientPrefabs).toEqual(serverPrefabs)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('client palette 이름이 server COLOR_PALETTES 와 일치', () => {
|
|
30
|
+
const clientPalettes = DEFAULT_CATALOG_ENTRIES.filter(e => e.kind === 'palette')
|
|
31
|
+
.map(e => e.name)
|
|
32
|
+
.sort()
|
|
33
|
+
const serverPalettes = listColorPaletteNames().sort()
|
|
34
|
+
expect(clientPalettes).toEqual(serverPalettes)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('client layout 이름이 server LAYOUT_TEMPLATES 와 일치', () => {
|
|
38
|
+
const clientLayouts = DEFAULT_CATALOG_ENTRIES.filter(e => e.kind === 'layout')
|
|
39
|
+
.map(e => e.name)
|
|
40
|
+
.sort()
|
|
41
|
+
const serverLayouts = listLayoutTemplateNames().sort()
|
|
42
|
+
expect(clientLayouts).toEqual(serverLayouts)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('각 항목이 description 보유 (popup UX)', () => {
|
|
46
|
+
for (const e of DEFAULT_CATALOG_ENTRIES) {
|
|
47
|
+
expect(e.description).toBeTruthy()
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('항목 이름 unique (kind 안에서)', () => {
|
|
52
|
+
const seen = new Set<string>()
|
|
53
|
+
for (const e of DEFAULT_CATALOG_ENTRIES) {
|
|
54
|
+
const key = `${e.kind}:${e.name}`
|
|
55
|
+
expect(seen.has(key)).toBe(false)
|
|
56
|
+
seen.add(key)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default 카탈로그 항목 (client-side hardcoded).
|
|
3
|
+
*
|
|
4
|
+
* 사용자 popup discoverability 용. server-side 카탈로그가 권위 source 이지만, 매번
|
|
5
|
+
* GraphQL 호출해 동적으로 가져오는 대신 default 항목을 client 에 캐시. 호스트 패키지
|
|
6
|
+
* (operato 등) 가 추가 prefab 등록 시 popup 에는 안 나타나지만, 사용자가 직접 이름을
|
|
7
|
+
* 타이핑하면 server-side resolver 가 정상 처리 (catalog-resolver.ts).
|
|
8
|
+
*
|
|
9
|
+
* Drift 경고 — 이 파일과 server-side 의 prefab-registry.ts / COLOR_PALETTES /
|
|
10
|
+
* LAYOUT_TEMPLATES 가 일치해야 한다. 새 default 추가 시 양쪽 동기 (assistant.test.ts
|
|
11
|
+
* 의 tool 카탈로그 회귀 테스트가 server-side drift 는 잡음, client 는 별도 검증
|
|
12
|
+
* 필요). 향후 GraphQL 'boardAICatalog' query 도입 시 이 파일 제거 가능.
|
|
13
|
+
*/
|
|
14
|
+
import type { CatalogEntry } from './mention-popup-helpers.js'
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_CATALOG_ENTRIES: CatalogEntry[] = [
|
|
17
|
+
// ── Prefabs (3) ─────────────────────────────────────────
|
|
18
|
+
{
|
|
19
|
+
name: 'monitor-card',
|
|
20
|
+
kind: 'prefab',
|
|
21
|
+
description: 'KPI 모니터링 카드 — 라벨 + 큰 값 + 부제'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'gauge-panel',
|
|
25
|
+
kind: 'prefab',
|
|
26
|
+
description: '원형 게이지 + 라벨 패널'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'stat-tile',
|
|
30
|
+
kind: 'prefab',
|
|
31
|
+
description: '강조 컬러 KPI 타일 (큰 숫자)'
|
|
32
|
+
},
|
|
33
|
+
// ── Palettes (7) ────────────────────────────────────────
|
|
34
|
+
{
|
|
35
|
+
name: 'industrial-steel',
|
|
36
|
+
kind: 'palette',
|
|
37
|
+
description: '제조 / 공장 — 강철 + cyan 강조'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'financial-trader',
|
|
41
|
+
kind: 'palette',
|
|
42
|
+
description: '금융 / 트레이딩 — navy + gold'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'control-room-dark',
|
|
46
|
+
kind: 'palette',
|
|
47
|
+
description: 'NOC / 관제실 — pure black + 네온'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'medical-clean',
|
|
51
|
+
kind: 'palette',
|
|
52
|
+
description: '의료 / 헬스케어 — soft white + blue'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'logistics-warm',
|
|
56
|
+
kind: 'palette',
|
|
57
|
+
description: '물류 / 운송 — warm earth + amber'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'neon-cyberpunk',
|
|
61
|
+
kind: 'palette',
|
|
62
|
+
description: 'SF / 사이버펑크 — black + magenta/cyan'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'paper-light',
|
|
66
|
+
kind: 'palette',
|
|
67
|
+
description: '보고서 / 프레젠테이션 — 종이 light'
|
|
68
|
+
},
|
|
69
|
+
// ── Layouts (6) ─────────────────────────────────────────
|
|
70
|
+
{
|
|
71
|
+
name: 'kpi-row',
|
|
72
|
+
kind: 'layout',
|
|
73
|
+
description: '상단 KPI 4-tile + 메인 영역'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: '3x3-grid',
|
|
77
|
+
kind: 'layout',
|
|
78
|
+
description: '균등 3×3 그리드 — 9개 동등 단위'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 't-shape-command',
|
|
82
|
+
kind: 'layout',
|
|
83
|
+
description: '관제실 — alarm bar + main + side'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'side-rail',
|
|
87
|
+
kind: 'layout',
|
|
88
|
+
description: '좌측 narrow + 메인 wide'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'split-half',
|
|
92
|
+
kind: 'layout',
|
|
93
|
+
description: '50/50 가로 분할 — 비교'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'header-content-footer',
|
|
97
|
+
kind: 'layout',
|
|
98
|
+
description: '보고서 형식 — 3단'
|
|
99
|
+
}
|
|
100
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* default-slash-templates.ts 정합성 검증.
|
|
3
|
+
*
|
|
4
|
+
* Slash template 은 server-side counterpart 없이 client-only — drift 검증 대상이
|
|
5
|
+
* 아니지만 자체 정합성 (unique name / 비어있지 않은 template / @ 멘션이 카탈로그
|
|
6
|
+
* 항목과 호환) 을 검증.
|
|
7
|
+
*/
|
|
8
|
+
import { DEFAULT_SLASH_TEMPLATES } from './default-slash-templates'
|
|
9
|
+
import { DEFAULT_CATALOG_ENTRIES } from './default-catalog-entries'
|
|
10
|
+
|
|
11
|
+
describe('Default slash templates — 정합성', () => {
|
|
12
|
+
test('각 템플릿이 name + template 보유', () => {
|
|
13
|
+
for (const t of DEFAULT_SLASH_TEMPLATES) {
|
|
14
|
+
expect(typeof t.name).toBe('string')
|
|
15
|
+
expect(t.name.length).toBeGreaterThan(0)
|
|
16
|
+
expect(typeof t.template).toBe('string')
|
|
17
|
+
expect(t.template.length).toBeGreaterThan(0)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('이름 unique', () => {
|
|
22
|
+
const seen = new Set<string>()
|
|
23
|
+
for (const t of DEFAULT_SLASH_TEMPLATES) {
|
|
24
|
+
expect(seen.has(t.name)).toBe(false)
|
|
25
|
+
seen.add(t.name)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('description 권장 (popup UX)', () => {
|
|
30
|
+
for (const t of DEFAULT_SLASH_TEMPLATES) {
|
|
31
|
+
expect(t.description).toBeTruthy()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('template 안의 @멘션은 카탈로그에 등록된 이름만 사용', () => {
|
|
36
|
+
const catalogNames = new Set(DEFAULT_CATALOG_ENTRIES.map(e => e.name))
|
|
37
|
+
const pattern = /@([^\s@]+)/g
|
|
38
|
+
for (const t of DEFAULT_SLASH_TEMPLATES) {
|
|
39
|
+
let m: RegExpExecArray | null
|
|
40
|
+
while ((m = pattern.exec(t.template)) !== null) {
|
|
41
|
+
const token = m[1]
|
|
42
|
+
// 알 수 없는 카탈로그 멘션이 들어가면 사용자가 선택해도 의도와 다른 결과
|
|
43
|
+
expect(catalogNames.has(token)).toBe(true)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('name 에는 # / @ / / 같은 trigger char 들어가면 안 됨', () => {
|
|
49
|
+
for (const t of DEFAULT_SLASH_TEMPLATES) {
|
|
50
|
+
expect(t.name).not.toContain('#')
|
|
51
|
+
expect(t.name).not.toContain('@')
|
|
52
|
+
expect(t.name).not.toContain('/')
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default slash templates — 사용자에게 빠르게 작성된 자연어 prompt 를 끼워주는 단축키.
|
|
3
|
+
*
|
|
4
|
+
* `/grid` → "선택한 컴포넌트들을 3x2 그리드로 정렬해줘" 같이 텍스트가 textarea 에
|
|
5
|
+
* 삽입됨. 사용자는 그대로 보내거나 숫자/카탈로그 이름을 수정해 보낼 수 있다.
|
|
6
|
+
*
|
|
7
|
+
* Slash 는 LLM 우회 명령이 아니다 — AI 가 받는 입력은 일반 자연어 prompt 와 동일.
|
|
8
|
+
* 단지 "이런 명령이 가능하구나" 의 discoverability 를 popup 으로 보여주는 역할.
|
|
9
|
+
*
|
|
10
|
+
* 수정 / 추가 정책:
|
|
11
|
+
* - 한국어 우선 (자연어가 한국어라야 한국 사용자에게 즉시 작동)
|
|
12
|
+
* - 카탈로그 이름 (@monitor-card 등) 가 들어간 템플릿은 server-side resolver 와
|
|
13
|
+
* 자동 호환 — drift 검증은 default-catalog-entries.test.ts 가 잡음
|
|
14
|
+
* - 너무 많이 만들지 말 것 (popup 이 과해짐). 가장 흔한 의도만.
|
|
15
|
+
*/
|
|
16
|
+
import type { SlashTemplate } from './mention-popup-helpers.js'
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_SLASH_TEMPLATES: SlashTemplate[] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'grid',
|
|
21
|
+
description: '선택한 컴포넌트들을 그리드로 정렬',
|
|
22
|
+
template: '선택한 컴포넌트들을 3x2 그리드로 정렬해줘'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'row',
|
|
26
|
+
description: '선택한 컴포넌트들을 한 줄로',
|
|
27
|
+
template: '선택한 컴포넌트들을 한 줄로 배치해줘'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'column',
|
|
31
|
+
description: '선택한 컴포넌트들을 세로로 일렬',
|
|
32
|
+
template: '선택한 컴포넌트들을 세로로 일렬 배치해줘'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'palette',
|
|
36
|
+
description: '검증된 색 팔레트로 통일',
|
|
37
|
+
template: '@industrial-steel 팔레트로 보드 전체 통일해줘'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'atmosphere',
|
|
41
|
+
description: '보드 분위기 변경 (자연어 자유 작성)',
|
|
42
|
+
template: '보드 분위기를 어둡고 차분하게 바꿔줘 (atmosphere primer 활용)'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'dashboard',
|
|
46
|
+
description: 'KPI 대시보드 기본 구성',
|
|
47
|
+
template: '@kpi-row 레이아웃으로 @stat-tile 4개를 상단에 + 아래 메인 영역 구성해줘'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'add-monitor',
|
|
51
|
+
description: '모니터링 카드 추가',
|
|
52
|
+
template: '@monitor-card 추가 (제목과 값은 적절히)'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'fit',
|
|
56
|
+
description: '보드 전체 보이게',
|
|
57
|
+
template: '보드 전체가 보이게 화면 맞춰줘'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'center',
|
|
61
|
+
description: '선택 컴포넌트 화면 가운데로',
|
|
62
|
+
template: '선택한 컴포넌트를 화면 가운데로 이동해줘'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'inspect',
|
|
66
|
+
description: '선택 컴포넌트 정보 보기',
|
|
67
|
+
template: '현재 선택된 컴포넌트의 모든 속성을 보여줘'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'align',
|
|
71
|
+
description: '선택 컴포넌트 정렬 (좌/우/위/아래/가운데)',
|
|
72
|
+
template: '선택한 컴포넌트들을 왼쪽 기준으로 정렬해줘'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'distribute',
|
|
76
|
+
description: '선택 컴포넌트 간격 균등 분배',
|
|
77
|
+
template: '선택한 컴포넌트들을 가로로 간격 균등하게 분배해줘'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'duplicate',
|
|
81
|
+
description: '선택 컴포넌트 복제',
|
|
82
|
+
template: '선택한 컴포넌트를 오른쪽에 같은 크기로 하나 더 복제해줘'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'match-size',
|
|
86
|
+
description: '선택 컴포넌트 크기 통일',
|
|
87
|
+
template: '선택한 컴포넌트들을 첫 번째와 같은 크기로 맞춰줘'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'z-front',
|
|
91
|
+
description: '선택 컴포넌트 맨 앞으로',
|
|
92
|
+
template: '선택한 컴포넌트를 z-order 맨 앞으로 가져와줘'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'rotate',
|
|
96
|
+
description: '선택 컴포넌트 회전',
|
|
97
|
+
template: '선택한 컴포넌트를 시계방향으로 90도 회전해줘'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'z-back',
|
|
101
|
+
description: '선택 컴포넌트 맨 뒤로',
|
|
102
|
+
template: '선택한 컴포넌트를 z-order 맨 뒤로 보내줘'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'mirror',
|
|
106
|
+
description: '선택 컴포넌트 좌우 반전',
|
|
107
|
+
template: '선택한 컴포넌트를 좌우 대칭으로 반전해줘'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'group',
|
|
111
|
+
description: '선택 컴포넌트 그룹화',
|
|
112
|
+
template: '선택한 컴포넌트들을 하나의 그룹으로 묶어줘'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'rename-sequential',
|
|
116
|
+
description: '선택 컴포넌트들에 순번 라벨',
|
|
117
|
+
template: '선택한 컴포넌트들에 1, 2, 3 순번 라벨을 붙여줘'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'bulk-set',
|
|
121
|
+
description: '선택 컴포넌트 속성 일괄 변경',
|
|
122
|
+
template: '선택한 컴포넌트들의 [속성]을 [값]으로 일괄 설정해줘'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'cleanup',
|
|
126
|
+
description: '겹침/어색한 위치 자동 정리',
|
|
127
|
+
template: '겹쳐있거나 어색한 위치의 컴포넌트들을 자연스럽게 정리해줘'
|
|
128
|
+
}
|
|
129
|
+
]
|