@k2b/ui 0.26.0 → 0.27.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 (65) hide show
  1. package/dist/browser/chat/ChatComposer.js +811 -406
  2. package/dist/browser/chat/ChatComposer.js.map +1 -1
  3. package/dist/browser/chat/chat-behavior.js +4 -0
  4. package/dist/browser/chat/chat-behavior.js.map +1 -1
  5. package/dist/browser/chat/chat-messages.js +14 -0
  6. package/dist/browser/chat/chat-messages.js.map +1 -1
  7. package/dist/browser/chat/command-search.js +170 -0
  8. package/dist/browser/chat/command-search.js.map +1 -0
  9. package/dist/browser/chat/composer-document.js +1 -1
  10. package/dist/browser/chat/composer-document.js.map +1 -1
  11. package/dist/browser/chat/index.js.map +1 -1
  12. package/dist/browser/content/CalendarDayView.js +28 -3
  13. package/dist/browser/content/CalendarDayView.js.map +1 -1
  14. package/dist/browser/inputs/Combobox.js +4 -2
  15. package/dist/browser/inputs/Combobox.js.map +1 -1
  16. package/dist/browser/inputs/SelectChip.js +43 -12
  17. package/dist/browser/inputs/SelectChip.js.map +1 -1
  18. package/dist/browser/layout/AppWorkspace.js +708 -515
  19. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  20. package/dist/browser/layout/BottomSheet.js +8 -0
  21. package/dist/browser/layout/BottomSheet.js.map +1 -1
  22. package/dist/browser/layout/app-workspace-controller.js +155 -23
  23. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  24. package/dist/browser/layout/app-workspace-state.js +5 -1
  25. package/dist/browser/layout/app-workspace-state.js.map +1 -1
  26. package/dist/browser/layout/index.js +1 -1
  27. package/dist/browser/layout/index.js.map +1 -1
  28. package/dist/ssr/chat/ChatComposer.js +410 -93
  29. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  30. package/dist/ssr/chat/chat-behavior.js +4 -0
  31. package/dist/ssr/chat/chat-behavior.js.map +1 -1
  32. package/dist/ssr/chat/chat-messages.js +14 -0
  33. package/dist/ssr/chat/chat-messages.js.map +1 -1
  34. package/dist/ssr/chat/command-search.js +170 -0
  35. package/dist/ssr/chat/command-search.js.map +1 -0
  36. package/dist/ssr/chat/composer-document.js +1 -1
  37. package/dist/ssr/chat/composer-document.js.map +1 -1
  38. package/dist/ssr/chat/index.js.map +1 -1
  39. package/dist/ssr/content/CalendarDayView.js +28 -3
  40. package/dist/ssr/content/CalendarDayView.js.map +1 -1
  41. package/dist/ssr/inputs/Combobox.js +4 -2
  42. package/dist/ssr/inputs/Combobox.js.map +1 -1
  43. package/dist/ssr/inputs/SelectChip.js +35 -11
  44. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  45. package/dist/ssr/layout/AppWorkspace.js +242 -105
  46. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  47. package/dist/ssr/layout/BottomSheet.js +8 -0
  48. package/dist/ssr/layout/BottomSheet.js.map +1 -1
  49. package/dist/ssr/layout/app-workspace-controller.js +155 -23
  50. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  51. package/dist/ssr/layout/app-workspace-state.js +5 -1
  52. package/dist/ssr/layout/app-workspace-state.js.map +1 -1
  53. package/dist/ssr/layout/index.js +1 -1
  54. package/dist/ssr/layout/index.js.map +1 -1
  55. package/dist/styles.css +1 -1
  56. package/dist/types/chat/ChatComposer.d.ts +44 -2
  57. package/dist/types/chat/chat-behavior.d.ts +4 -0
  58. package/dist/types/chat/chat-messages.d.ts +7 -0
  59. package/dist/types/chat/command-search.d.ts +39 -0
  60. package/dist/types/chat/index.d.ts +1 -1
  61. package/dist/types/inputs/SelectChip.d.ts +4 -0
  62. package/dist/types/layout/AppWorkspace.d.ts +13 -1
  63. package/dist/types/layout/BottomSheet.d.ts +8 -0
  64. package/dist/types/layout/index.d.ts +1 -1
  65. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["chat/chat-behavior.ts"],"sourcesContent":["import type { ChatAction } from \"./types\";\n\nexport const nextChatCommandIndex = (index: number, length: number, direction: 1 | -1): number =>\n length <= 0 ? 0 : (index + direction + length) % length;\n\nexport const isChatNearBottom = (scrollHeight: number, scrollTop: number, clientHeight: number, threshold = 96): boolean =>\n scrollHeight - scrollTop - clientHeight <= threshold;\n\nexport const restoredChatScrollTop = (previousScrollTop: number, previousScrollHeight: number, nextScrollHeight: number): number =>\n Math.max(0, previousScrollTop + nextScrollHeight - previousScrollHeight);\n\nexport type ChatSubmissionRun = {\n /** Clears the controlled draft (and attachments) before the handler runs. */\n clear: () => void;\n /**\n * The application handler. A synchronous throw, a rejected promise, and a\n * `false` result all count as a rejected submission.\n */\n perform: () => boolean | void | Promise<boolean | void>;\n /** Restores the cleared draft (and attachments) after a rejected submission. */\n restore: () => void;\n onError?: (error: unknown) => void;\n};\n\n/**\n * Runs one composer submission with optimistic clearing. Resolves to `true`\n * when the handler accepted the submission. A synchronous throw is handled\n * exactly like a rejected promise so a controlled draft is never lost.\n */\nexport const runChatSubmission = async (run: ChatSubmissionRun): Promise<boolean> => {\n run.clear();\n try {\n if ((await run.perform()) === false) {\n run.restore();\n return false;\n }\n return true;\n } catch (error) {\n run.onError?.(error);\n run.restore();\n return false;\n }\n};\n\n/** Reports a handler failure without letting a synchronous throw escape. */\nexport const reportChatFailure = (perform: () => unknown, onError?: (error: unknown) => void): void => {\n try {\n const result = perform();\n if (result instanceof Promise) void result.catch((error) => onError?.(error));\n } catch (error) {\n onError?.(error);\n }\n};\n\nconst writeChatClipboard = async (value: string): Promise<void> => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n throw new Error(\"Clipboard access is unavailable.\");\n }\n await navigator.clipboard.writeText(value);\n};\n\n/** Executes the one behavior guaranteed by the ChatAction contract. */\nexport const executeChatAction = async (action: ChatAction): Promise<void> => {\n if (typeof action.copyText === \"string\") {\n await writeChatClipboard(action.copyText);\n return;\n }\n await action.onSelect();\n};\n\nexport type ChatEnterKey = { shiftKey: boolean; ctrlKey: boolean; metaKey: boolean; altKey: boolean };\n\n/**\n * What Enter does in a conversation composer: Ctrl/⌘+Enter always sends; Shift+Enter, Enter inside an open code\n * block, and Enter on a touch-only device break the line; otherwise `sendKey` decides. IME input is the caller's check.\n */\nexport const conversationEnterAction = (\n key: ChatEnterKey,\n context: { sendKey: \"enter\" | \"mod-enter\"; touchOnly: boolean; inCodeBlock: boolean },\n): \"send\" | \"newline\" => {\n if (key.ctrlKey || key.metaKey) return \"send\";\n if (key.shiftKey || key.altKey || context.touchOnly || context.inCodeBlock || context.sendKey === \"mod-enter\") return \"newline\";\n return \"send\";\n};\n"],"mappings":"AAEA,OAAO,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,SAAiB,KACnF,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,GAAG,MAAM,IAAI,MAAM;AAEzD,OAAO,MAAM,gBAAgB,GAAG,CAAC,YAAoB,EAAE,SAAiB,EAAE,YAAoB,EAAE,SAAS,GAAG,EAAE,KAC5G,YAAY,GAAG,SAAS,GAAG,YAAY,IAAI,SAAS;AAEtD,OAAO,MAAM,qBAAqB,GAAG,CAAC,iBAAyB,EAAE,oBAA4B,EAAE,gBAAwB,KACrH,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAe1E;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,iBAAiB,GAAG,MAAO,GAAsB,IAAuB;EACnF,GAAG,CAAC,KAAK,CAAC,CAAC;EACX,IAAI;IACF,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,EAAE;MACnC,GAAG,CAAC,OAAO,CAAC,CAAC;MACb,OAAO,KAAK;IACd;IACA,OAAO,IAAI;EACb,CAAC,CAAC,OAAO,KAAK,EAAE;IACd,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;IACpB,GAAG,CAAC,OAAO,CAAC,CAAC;IACb,OAAO,KAAK;EACd;AACF,CAAC;;AAED;AACA,OAAO,MAAM,iBAAiB,GAAG,CAAC,OAAsB,EAAE,OAAkC,KAAW;EACrG,IAAI;IACF,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC;IACxB,IAAI,MAAM,YAAY,OAAO,EAAE,KAAK,MAAM,CAAC,KAAK,CAAE,KAAK,IAAK,OAAO,GAAG,KAAK,CAAC,CAAC;EAC/E,CAAC,CAAC,OAAO,KAAK,EAAE;IACd,OAAO,GAAG,KAAK,CAAC;EAClB;AACF,CAAC;AAED,MAAM,kBAAkB,GAAG,MAAO,KAAa,IAAoB;EACjE,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE;IACvE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC;EACrD;EACA,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;AAC5C,CAAC;;AAED;AACA,OAAO,MAAM,iBAAiB,GAAG,MAAO,MAAkB,IAAoB;EAC5E,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE;IACvC,MAAM,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;IACzC;EACF;EACA,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAID;AACA;AACA;AACA;AACA,OAAO,MAAM,uBAAuB,GAAG,CACrC,GAAiB,EACjB,OAAqF,KAC9D;EACvB,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,MAAM;EAC7C,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,KAAK,WAAW,EAAE,OAAO,SAAS;EAC/H,OAAO,MAAM;AACf,CAAC","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["chat/chat-behavior.ts"],"sourcesContent":["import type { ChatCommand } from \"./ChatComposer\";\nimport type { ChatAction } from \"./types\";\n\nexport const nextChatCommandIndex = (index: number, length: number, direction: 1 | -1): number =>\n length <= 0 ? 0 : (index + direction + length) % length;\n\nexport const isChatNearBottom = (scrollHeight: number, scrollTop: number, clientHeight: number, threshold = 96): boolean =>\n scrollHeight - scrollTop - clientHeight <= threshold;\n\nexport const restoredChatScrollTop = (previousScrollTop: number, previousScrollHeight: number, nextScrollHeight: number): number =>\n Math.max(0, previousScrollTop + nextScrollHeight - previousScrollHeight);\n\nexport type ChatSubmissionRun = {\n /** Clears the controlled draft (and attachments) before the handler runs. */\n clear: () => void;\n /**\n * The application handler. A synchronous throw, a rejected promise, and a\n * `false` result all count as a rejected submission.\n */\n perform: () => boolean | void | Promise<boolean | void>;\n /** Restores the cleared draft (and attachments) after a rejected submission. */\n restore: () => void;\n onError?: (error: unknown) => void;\n};\n\n/**\n * Runs one composer submission with optimistic clearing. Resolves to `true`\n * when the handler accepted the submission. A synchronous throw is handled\n * exactly like a rejected promise so a controlled draft is never lost.\n */\nexport const runChatSubmission = async (run: ChatSubmissionRun): Promise<boolean> => {\n run.clear();\n try {\n if ((await run.perform()) === false) {\n run.restore();\n return false;\n }\n return true;\n } catch (error) {\n run.onError?.(error);\n run.restore();\n return false;\n }\n};\n\n/** Reports a handler failure without letting a synchronous throw escape. */\nexport const reportChatFailure = (perform: () => unknown, onError?: (error: unknown) => void): void => {\n try {\n const result = perform();\n if (result instanceof Promise) void result.catch((error) => onError?.(error));\n } catch (error) {\n onError?.(error);\n }\n};\n\nconst writeChatClipboard = async (value: string): Promise<void> => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n throw new Error(\"Clipboard access is unavailable.\");\n }\n await navigator.clipboard.writeText(value);\n};\n\n/** Executes the one behavior guaranteed by the ChatAction contract. */\nexport const executeChatAction = async (action: ChatAction): Promise<void> => {\n if (typeof action.copyText === \"string\") {\n await writeChatClipboard(action.copyText);\n return;\n }\n await action.onSelect();\n};\n\nexport type ChatEnterKey = { shiftKey: boolean; ctrlKey: boolean; metaKey: boolean; altKey: boolean };\n\n/**\n * What Enter does in a conversation composer: Ctrl/⌘+Enter always sends; Shift+Enter, Enter inside an open code\n * block, and Enter on a touch-only device break the line; otherwise `sendKey` decides. IME input is the caller's check.\n */\nexport const conversationEnterAction = (\n key: ChatEnterKey,\n context: { sendKey: \"enter\" | \"mod-enter\"; touchOnly: boolean; inCodeBlock: boolean },\n): \"send\" | \"newline\" => {\n if (key.ctrlKey || key.metaKey) return \"send\";\n if (key.shiftKey || key.altKey || context.touchOnly || context.inCodeBlock || context.sendKey === \"mod-enter\") return \"newline\";\n return \"send\";\n};\n\n/** Case- and accent-insensitive text for command discovery. */\nexport const foldChatSearchText = (text: string): string => text.toLowerCase().normalize(\"NFD\").replace(/\\p{M}/gu, \"\").replace(/ß/g, \"ss\");\n\nexport const chatCommandKey = (command: ChatCommand): string => command.id ?? command.name;\n"],"mappings":"AAGA,OAAO,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,SAAiB,KACnF,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,GAAG,MAAM,IAAI,MAAM;AAEzD,OAAO,MAAM,gBAAgB,GAAG,CAAC,YAAoB,EAAE,SAAiB,EAAE,YAAoB,EAAE,SAAS,GAAG,EAAE,KAC5G,YAAY,GAAG,SAAS,GAAG,YAAY,IAAI,SAAS;AAEtD,OAAO,MAAM,qBAAqB,GAAG,CAAC,iBAAyB,EAAE,oBAA4B,EAAE,gBAAwB,KACrH,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAe1E;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,iBAAiB,GAAG,MAAO,GAAsB,IAAuB;EACnF,GAAG,CAAC,KAAK,CAAC,CAAC;EACX,IAAI;IACF,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,EAAE;MACnC,GAAG,CAAC,OAAO,CAAC,CAAC;MACb,OAAO,KAAK;IACd;IACA,OAAO,IAAI;EACb,CAAC,CAAC,OAAO,KAAK,EAAE;IACd,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;IACpB,GAAG,CAAC,OAAO,CAAC,CAAC;IACb,OAAO,KAAK;EACd;AACF,CAAC;;AAED;AACA,OAAO,MAAM,iBAAiB,GAAG,CAAC,OAAsB,EAAE,OAAkC,KAAW;EACrG,IAAI;IACF,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC;IACxB,IAAI,MAAM,YAAY,OAAO,EAAE,KAAK,MAAM,CAAC,KAAK,CAAE,KAAK,IAAK,OAAO,GAAG,KAAK,CAAC,CAAC;EAC/E,CAAC,CAAC,OAAO,KAAK,EAAE;IACd,OAAO,GAAG,KAAK,CAAC;EAClB;AACF,CAAC;AAED,MAAM,kBAAkB,GAAG,MAAO,KAAa,IAAoB;EACjE,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE;IACvE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC;EACrD;EACA,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;AAC5C,CAAC;;AAED;AACA,OAAO,MAAM,iBAAiB,GAAG,MAAO,MAAkB,IAAoB;EAC5E,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE;IACvC,MAAM,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;IACzC;EACF;EACA,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAID;AACA;AACA;AACA;AACA,OAAO,MAAM,uBAAuB,GAAG,CACrC,GAAiB,EACjB,OAAqF,KAC9D;EACvB,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,MAAM;EAC7C,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,KAAK,WAAW,EAAE,OAAO,SAAS;EAC/H,OAAO,MAAM;AACf,CAAC;;AAED;AACA,OAAO,MAAM,kBAAkB,GAAI,IAAY,IAAa,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC;AAE1I,OAAO,MAAM,cAAc,GAAI,OAAoB,IAAa,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI","ignoreList":[]}
@@ -4,6 +4,7 @@ const chatMessages = i18n.define({
4
4
  baseLocale: "en",
5
5
  messages: {
6
6
  en: {
7
+ commandFailed: "That did not work.",
7
8
  attachments: "Attachments",
8
9
  assistant: "Assistant",
9
10
  addToChat: "Add to chat",
@@ -12,6 +13,12 @@ const chatMessages = i18n.define({
12
13
  chat: "Chat",
13
14
  chooseModel: "Choose model",
14
15
  commands: "Commands",
16
+ commandsNothingFound: ({
17
+ query
18
+ }) => `Nothing found for “${query}”`,
19
+ commandSuggestions: ({
20
+ count
21
+ }) => count === 0 ? "No suggestions" : count === 1 ? "1 suggestion" : `${count} suggestions`,
15
22
  dropFilesToAttach: "Drop to attach to the message",
16
23
  conversation: "Conversation",
17
24
  jumpToLatest: "Jump to latest",
@@ -115,6 +122,7 @@ const chatMessages = i18n.define({
115
122
  stopping: "Stopping"
116
123
  },
117
124
  de: {
125
+ commandFailed: "Das hat nicht geklappt.",
118
126
  attachments: "Anhänge",
119
127
  assistant: "Assistent",
120
128
  addToChat: "Zum Chat hinzufügen",
@@ -123,6 +131,12 @@ const chatMessages = i18n.define({
123
131
  chat: "Chat",
124
132
  chooseModel: "Modell auswählen",
125
133
  commands: "Befehle",
134
+ commandsNothingFound: ({
135
+ query
136
+ }) => `Nichts gefunden für „${query}“`,
137
+ commandSuggestions: ({
138
+ count
139
+ }) => count === 0 ? "Keine Vorschläge" : count === 1 ? "1 Vorschlag" : `${count} Vorschläge`,
126
140
  dropFilesToAttach: "Ablegen, um an die Nachricht anzuhängen",
127
141
  conversation: "Unterhaltung",
128
142
  jumpToLatest: "Zur neuesten Nachricht",
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["chat/chat-messages.ts"],"sourcesContent":["import { i18n } from \"@k2b/stdlib\";\nimport { useLocale } from \"../intl/locale\";\n\nconst chatMessages = i18n.define({\n baseLocale: \"en\",\n messages: {\n en: {\n attachments: \"Attachments\",\n assistant: \"Assistant\",\n addToChat: \"Add to chat\",\n attachFiles: \"Attach files\",\n addGuidance: \"Add guidance...\",\n chat: \"Chat\",\n chooseModel: \"Choose model\",\n commands: \"Commands\",\n dropFilesToAttach: \"Drop to attach to the message\",\n conversation: \"Conversation\",\n jumpToLatest: \"Jump to latest\",\n messageSent: \"Sent\",\n messageNotSent: \"Not sent\",\n messageRetry: \"Retry\",\n messageShowMore: \"Show more\",\n messageShowLess: \"Show less\",\n messageReplyTo: \"In reply to\",\n messageForwarded: \"Forwarded\",\n messageEdited: \"edited\",\n messageDeleted: \"This message was deleted\",\n messageVideo: \"Video\",\n messageMoreAttachments: ({ count }: { count: number }) => `${count} more`,\n messageReactions: \"Reactions\",\n messageReaction: ({ emoji, count, names, own }: { emoji: string; count: number; names?: string; own?: boolean }) =>\n `${emoji} ${own && count === 1 ? \"your reaction\" : `${count} ${count === 1 ? \"reaction\" : \"reactions\"}${own ? \", including yours\" : \"\"}`}${names ? `: ${names}` : \"\"}`,\n messageAddReaction: \"Add reaction\",\n messageReplies: ({ count }: { count: number }) => (count === 1 ? \"1 reply\" : `${count} replies`),\n messageLastReply: ({ time }: { time: string }) => `Last reply ${time}`,\n messageStop: \"Stop\",\n conversationMessages: ({ label }: { label: string }) => `${label} messages`,\n loadEarlier: \"Load earlier\",\n loadingConversation: \"Loading conversation\",\n loadingMessages: \"Loading messages\",\n failed: \"Failed\",\n generating: \"Generating\",\n loadOlderMessages: \"Load older messages\",\n loadingOlderMessages: \"Loading older messages\",\n couldNotLoadOlderMessages: \"Could not load older messages\",\n couldNotLoadEarlierMessages: \"Could not load earlier messages\",\n couldNotPostMessage: \"Could not post message\",\n messageActions: \"Message actions\",\n messageComposer: \"Message composer\",\n messageInput: \"Message input\",\n model: \"Model\",\n input: \"Input\",\n output: \"Output\",\n loopTotal: \"Whole response\",\n remaining: \"Remaining\",\n notConfigured: \"Not configured\",\n system: \"System\",\n tool: \"Tool\",\n user: \"User\",\n waiting: \"Waiting\",\n lastRequestContext: \"Last request context\",\n contextWindowUsed: \"Context window used\",\n contextWindow: \"Context window\",\n contextUsageUnavailable: \"Context usage unavailable\",\n contextUsageUnavailableWithWindow: ({ window }: { window: string }) => `Context usage unavailable, ${window} token context window`,\n tokensUsed: ({ total }: { total: string }) => `${total} tokens used`,\n tokensUsedPercent: ({ total, percent }: { total: string; percent: number }) =>\n `${total} tokens used, ${percent}% of the context window`,\n postMessage: \"Post message\",\n queueMessage: \"Queue message\",\n queueing: \"Queueing\",\n sending: \"Sending\",\n steering: \"Steering\",\n writeMessage: \"Write a message or type / ...\",\n writeConversationMessage: \"Write a message\",\n composerFormatting: \"Formatting\",\n insertEmoji: \"Insert emoji\",\n emojiSuggestions: \"Emoji suggestions\",\n dictate: \"Dictate\",\n dictateOrHold: \"Dictate. Hold to record a voice message.\",\n stopDictation: \"Stop dictation\",\n microphoneOptions: \"Microphone options\",\n recordVoiceMessage: \"Record voice message\",\n dictationListening: \"Dictating …\",\n dictationRefining: \"Refining the text …\",\n dictationRefined: \"Text refined\",\n dictationUnrefined: \"Text not refined\",\n dictationInterrupted: \"Dictation interrupted\",\n restoreOriginal: \"Restore original\",\n openInNewTab: ({ name }: { name: string }) => `Open ${name} in a new tab`,\n retryOlderMessages: \"Retry loading older messages\",\n sendMessage: \"Send message\",\n startConversation: \"Start a conversation\",\n steerResponse: \"Steer response\",\n stopResponse: \"Stop response\",\n stopping: \"Stopping\",\n },\n de: {\n attachments: \"Anhänge\",\n assistant: \"Assistent\",\n addToChat: \"Zum Chat hinzufügen\",\n attachFiles: \"Dateien anhängen\",\n addGuidance: \"Hinweis hinzufügen...\",\n chat: \"Chat\",\n chooseModel: \"Modell auswählen\",\n commands: \"Befehle\",\n dropFilesToAttach: \"Ablegen, um an die Nachricht anzuhängen\",\n conversation: \"Unterhaltung\",\n jumpToLatest: \"Zur neuesten Nachricht\",\n messageSent: \"Gesendet\",\n messageNotSent: \"Nicht gesendet\",\n messageRetry: \"Erneut senden\",\n messageShowMore: \"Mehr anzeigen\",\n messageShowLess: \"Weniger anzeigen\",\n messageReplyTo: \"Antwort auf\",\n messageForwarded: \"Weitergeleitet\",\n messageEdited: \"bearbeitet\",\n messageDeleted: \"Diese Nachricht wurde gelöscht\",\n messageVideo: \"Video\",\n messageMoreAttachments: ({ count }) => `${count} weitere`,\n messageReactions: \"Reaktionen\",\n messageReaction: ({ emoji, count, names, own }) =>\n `${emoji} ${own && count === 1 ? \"deine Reaktion\" : `${count} ${count === 1 ? \"Reaktion\" : \"Reaktionen\"}${own ? \", einschließlich deiner\" : \"\"}`}${names ? `: ${names}` : \"\"}`,\n messageAddReaction: \"Reaktion hinzufügen\",\n messageReplies: ({ count }) => (count === 1 ? \"1 Antwort\" : `${count} Antworten`),\n messageLastReply: ({ time }) => `Letzte Antwort ${time}`,\n messageStop: \"Stoppen\",\n conversationMessages: ({ label }) => `Nachrichten in ${label}`,\n loadEarlier: \"Ältere laden\",\n loadingConversation: \"Unterhaltung wird geladen\",\n loadingMessages: \"Nachrichten werden geladen\",\n failed: \"Fehlgeschlagen\",\n generating: \"Wird generiert\",\n loadOlderMessages: \"Ältere Nachrichten laden\",\n loadingOlderMessages: \"Ältere Nachrichten werden geladen\",\n couldNotLoadOlderMessages: \"Ältere Nachrichten konnten nicht geladen werden\",\n couldNotLoadEarlierMessages: \"Ältere Nachrichten konnten nicht geladen werden\",\n couldNotPostMessage: \"Nachricht konnte nicht gepostet werden\",\n messageActions: \"Nachrichtenaktionen\",\n messageComposer: \"Nachricht verfassen\",\n messageInput: \"Nachrichteneingabe\",\n model: \"Modell\",\n input: \"Eingabe\",\n output: \"Ausgabe\",\n loopTotal: \"Ganze Antwort\",\n remaining: \"Verbleibend\",\n notConfigured: \"Nicht konfiguriert\",\n system: \"System\",\n tool: \"Werkzeug\",\n user: \"Benutzer\",\n waiting: \"Wartet\",\n lastRequestContext: \"Kontext der letzten Anfrage\",\n contextWindowUsed: \"Kontextfenster verwendet\",\n contextWindow: \"Kontextfenster\",\n contextUsageUnavailable: \"Kontextnutzung nicht verfügbar\",\n contextUsageUnavailableWithWindow: ({ window }) => `Kontextnutzung nicht verfügbar, Kontextfenster mit ${window} Token`,\n tokensUsed: ({ total }) => `${total} Token verwendet`,\n tokensUsedPercent: ({ total, percent }) => `${total} Token verwendet, ${percent}% des Kontextfensters`,\n postMessage: \"Nachricht posten\",\n queueMessage: \"Nachricht einreihen\",\n queueing: \"Wird eingereiht\",\n sending: \"Wird gesendet\",\n steering: \"Wird gesteuert\",\n writeMessage: \"Nachricht schreiben oder / eingeben ...\",\n writeConversationMessage: \"Nachricht schreiben\",\n composerFormatting: \"Formatierung\",\n insertEmoji: \"Emoji einfügen\",\n emojiSuggestions: \"Emoji-Vorschläge\",\n dictate: \"Diktieren\",\n dictateOrHold: \"Diktieren. Halten nimmt eine Sprachnachricht auf.\",\n stopDictation: \"Diktat beenden\",\n microphoneOptions: \"Mikrofon-Optionen\",\n recordVoiceMessage: \"Sprachnachricht aufnehmen\",\n dictationListening: \"Diktat läuft …\",\n dictationRefining: \"Text wird verfeinert …\",\n dictationRefined: \"Text verfeinert\",\n dictationUnrefined: \"Text nicht verfeinert\",\n dictationInterrupted: \"Diktat unterbrochen\",\n restoreOriginal: \"Original wiederherstellen\",\n openInNewTab: ({ name }) => `${name} in neuem Tab öffnen`,\n retryOlderMessages: \"Ältere Nachrichten erneut laden\",\n sendMessage: \"Nachricht senden\",\n startConversation: \"Unterhaltung beginnen\",\n steerResponse: \"Antwort steuern\",\n stopResponse: \"Antwort stoppen\",\n stopping: \"Wird gestoppt\",\n },\n },\n});\n\nexport type ChatMessages = ReturnType<(typeof chatMessages)[\"resolve\"]>[\"t\"];\n\n/** Resolved from the inherited render locale. */\nexport const useChatMessages = (): (() => ChatMessages) => {\n const locale = useLocale();\n return () => chatMessages.resolve([locale()]).t;\n};\n\nexport const checkChatMessages = () => chatMessages.check();\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["chat/chat-messages.ts"],"sourcesContent":["import { i18n } from \"@k2b/stdlib\";\nimport { useLocale } from \"../intl/locale\";\n\nconst chatMessages = i18n.define({\n baseLocale: \"en\",\n messages: {\n en: {\n commandFailed: \"That did not work.\",\n attachments: \"Attachments\",\n assistant: \"Assistant\",\n addToChat: \"Add to chat\",\n attachFiles: \"Attach files\",\n addGuidance: \"Add guidance...\",\n chat: \"Chat\",\n chooseModel: \"Choose model\",\n commands: \"Commands\",\n commandsNothingFound: ({ query }: { query: string }) => `Nothing found for “${query}”`,\n commandSuggestions: ({ count }: { count: number }) =>\n count === 0 ? \"No suggestions\" : count === 1 ? \"1 suggestion\" : `${count} suggestions`,\n dropFilesToAttach: \"Drop to attach to the message\",\n conversation: \"Conversation\",\n jumpToLatest: \"Jump to latest\",\n messageSent: \"Sent\",\n messageNotSent: \"Not sent\",\n messageRetry: \"Retry\",\n messageShowMore: \"Show more\",\n messageShowLess: \"Show less\",\n messageReplyTo: \"In reply to\",\n messageForwarded: \"Forwarded\",\n messageEdited: \"edited\",\n messageDeleted: \"This message was deleted\",\n messageVideo: \"Video\",\n messageMoreAttachments: ({ count }: { count: number }) => `${count} more`,\n messageReactions: \"Reactions\",\n messageReaction: ({ emoji, count, names, own }: { emoji: string; count: number; names?: string; own?: boolean }) =>\n `${emoji} ${own && count === 1 ? \"your reaction\" : `${count} ${count === 1 ? \"reaction\" : \"reactions\"}${own ? \", including yours\" : \"\"}`}${names ? `: ${names}` : \"\"}`,\n messageAddReaction: \"Add reaction\",\n messageReplies: ({ count }: { count: number }) => (count === 1 ? \"1 reply\" : `${count} replies`),\n messageLastReply: ({ time }: { time: string }) => `Last reply ${time}`,\n messageStop: \"Stop\",\n conversationMessages: ({ label }: { label: string }) => `${label} messages`,\n loadEarlier: \"Load earlier\",\n loadingConversation: \"Loading conversation\",\n loadingMessages: \"Loading messages\",\n failed: \"Failed\",\n generating: \"Generating\",\n loadOlderMessages: \"Load older messages\",\n loadingOlderMessages: \"Loading older messages\",\n couldNotLoadOlderMessages: \"Could not load older messages\",\n couldNotLoadEarlierMessages: \"Could not load earlier messages\",\n couldNotPostMessage: \"Could not post message\",\n messageActions: \"Message actions\",\n messageComposer: \"Message composer\",\n messageInput: \"Message input\",\n model: \"Model\",\n input: \"Input\",\n output: \"Output\",\n loopTotal: \"Whole response\",\n remaining: \"Remaining\",\n notConfigured: \"Not configured\",\n system: \"System\",\n tool: \"Tool\",\n user: \"User\",\n waiting: \"Waiting\",\n lastRequestContext: \"Last request context\",\n contextWindowUsed: \"Context window used\",\n contextWindow: \"Context window\",\n contextUsageUnavailable: \"Context usage unavailable\",\n contextUsageUnavailableWithWindow: ({ window }: { window: string }) => `Context usage unavailable, ${window} token context window`,\n tokensUsed: ({ total }: { total: string }) => `${total} tokens used`,\n tokensUsedPercent: ({ total, percent }: { total: string; percent: number }) =>\n `${total} tokens used, ${percent}% of the context window`,\n postMessage: \"Post message\",\n queueMessage: \"Queue message\",\n queueing: \"Queueing\",\n sending: \"Sending\",\n steering: \"Steering\",\n writeMessage: \"Write a message or type / ...\",\n writeConversationMessage: \"Write a message\",\n composerFormatting: \"Formatting\",\n insertEmoji: \"Insert emoji\",\n emojiSuggestions: \"Emoji suggestions\",\n dictate: \"Dictate\",\n dictateOrHold: \"Dictate. Hold to record a voice message.\",\n stopDictation: \"Stop dictation\",\n microphoneOptions: \"Microphone options\",\n recordVoiceMessage: \"Record voice message\",\n dictationListening: \"Dictating …\",\n dictationRefining: \"Refining the text …\",\n dictationRefined: \"Text refined\",\n dictationUnrefined: \"Text not refined\",\n dictationInterrupted: \"Dictation interrupted\",\n restoreOriginal: \"Restore original\",\n openInNewTab: ({ name }: { name: string }) => `Open ${name} in a new tab`,\n retryOlderMessages: \"Retry loading older messages\",\n sendMessage: \"Send message\",\n startConversation: \"Start a conversation\",\n steerResponse: \"Steer response\",\n stopResponse: \"Stop response\",\n stopping: \"Stopping\",\n },\n de: {\n commandFailed: \"Das hat nicht geklappt.\",\n attachments: \"Anhänge\",\n assistant: \"Assistent\",\n addToChat: \"Zum Chat hinzufügen\",\n attachFiles: \"Dateien anhängen\",\n addGuidance: \"Hinweis hinzufügen...\",\n chat: \"Chat\",\n chooseModel: \"Modell auswählen\",\n commands: \"Befehle\",\n commandsNothingFound: ({ query }) => `Nichts gefunden für „${query}“`,\n commandSuggestions: ({ count }) => (count === 0 ? \"Keine Vorschläge\" : count === 1 ? \"1 Vorschlag\" : `${count} Vorschläge`),\n dropFilesToAttach: \"Ablegen, um an die Nachricht anzuhängen\",\n conversation: \"Unterhaltung\",\n jumpToLatest: \"Zur neuesten Nachricht\",\n messageSent: \"Gesendet\",\n messageNotSent: \"Nicht gesendet\",\n messageRetry: \"Erneut senden\",\n messageShowMore: \"Mehr anzeigen\",\n messageShowLess: \"Weniger anzeigen\",\n messageReplyTo: \"Antwort auf\",\n messageForwarded: \"Weitergeleitet\",\n messageEdited: \"bearbeitet\",\n messageDeleted: \"Diese Nachricht wurde gelöscht\",\n messageVideo: \"Video\",\n messageMoreAttachments: ({ count }) => `${count} weitere`,\n messageReactions: \"Reaktionen\",\n messageReaction: ({ emoji, count, names, own }) =>\n `${emoji} ${own && count === 1 ? \"deine Reaktion\" : `${count} ${count === 1 ? \"Reaktion\" : \"Reaktionen\"}${own ? \", einschließlich deiner\" : \"\"}`}${names ? `: ${names}` : \"\"}`,\n messageAddReaction: \"Reaktion hinzufügen\",\n messageReplies: ({ count }) => (count === 1 ? \"1 Antwort\" : `${count} Antworten`),\n messageLastReply: ({ time }) => `Letzte Antwort ${time}`,\n messageStop: \"Stoppen\",\n conversationMessages: ({ label }) => `Nachrichten in ${label}`,\n loadEarlier: \"Ältere laden\",\n loadingConversation: \"Unterhaltung wird geladen\",\n loadingMessages: \"Nachrichten werden geladen\",\n failed: \"Fehlgeschlagen\",\n generating: \"Wird generiert\",\n loadOlderMessages: \"Ältere Nachrichten laden\",\n loadingOlderMessages: \"Ältere Nachrichten werden geladen\",\n couldNotLoadOlderMessages: \"Ältere Nachrichten konnten nicht geladen werden\",\n couldNotLoadEarlierMessages: \"Ältere Nachrichten konnten nicht geladen werden\",\n couldNotPostMessage: \"Nachricht konnte nicht gepostet werden\",\n messageActions: \"Nachrichtenaktionen\",\n messageComposer: \"Nachricht verfassen\",\n messageInput: \"Nachrichteneingabe\",\n model: \"Modell\",\n input: \"Eingabe\",\n output: \"Ausgabe\",\n loopTotal: \"Ganze Antwort\",\n remaining: \"Verbleibend\",\n notConfigured: \"Nicht konfiguriert\",\n system: \"System\",\n tool: \"Werkzeug\",\n user: \"Benutzer\",\n waiting: \"Wartet\",\n lastRequestContext: \"Kontext der letzten Anfrage\",\n contextWindowUsed: \"Kontextfenster verwendet\",\n contextWindow: \"Kontextfenster\",\n contextUsageUnavailable: \"Kontextnutzung nicht verfügbar\",\n contextUsageUnavailableWithWindow: ({ window }) => `Kontextnutzung nicht verfügbar, Kontextfenster mit ${window} Token`,\n tokensUsed: ({ total }) => `${total} Token verwendet`,\n tokensUsedPercent: ({ total, percent }) => `${total} Token verwendet, ${percent}% des Kontextfensters`,\n postMessage: \"Nachricht posten\",\n queueMessage: \"Nachricht einreihen\",\n queueing: \"Wird eingereiht\",\n sending: \"Wird gesendet\",\n steering: \"Wird gesteuert\",\n writeMessage: \"Nachricht schreiben oder / eingeben ...\",\n writeConversationMessage: \"Nachricht schreiben\",\n composerFormatting: \"Formatierung\",\n insertEmoji: \"Emoji einfügen\",\n emojiSuggestions: \"Emoji-Vorschläge\",\n dictate: \"Diktieren\",\n dictateOrHold: \"Diktieren. Halten nimmt eine Sprachnachricht auf.\",\n stopDictation: \"Diktat beenden\",\n microphoneOptions: \"Mikrofon-Optionen\",\n recordVoiceMessage: \"Sprachnachricht aufnehmen\",\n dictationListening: \"Diktat läuft …\",\n dictationRefining: \"Text wird verfeinert …\",\n dictationRefined: \"Text verfeinert\",\n dictationUnrefined: \"Text nicht verfeinert\",\n dictationInterrupted: \"Diktat unterbrochen\",\n restoreOriginal: \"Original wiederherstellen\",\n openInNewTab: ({ name }) => `${name} in neuem Tab öffnen`,\n retryOlderMessages: \"Ältere Nachrichten erneut laden\",\n sendMessage: \"Nachricht senden\",\n startConversation: \"Unterhaltung beginnen\",\n steerResponse: \"Antwort steuern\",\n stopResponse: \"Antwort stoppen\",\n stopping: \"Wird gestoppt\",\n },\n },\n});\n\nexport type ChatMessages = ReturnType<(typeof chatMessages)[\"resolve\"]>[\"t\"];\n\n/** Resolved from the inherited render locale. */\nexport const useChatMessages = (): (() => ChatMessages) => {\n const locale = useLocale();\n return () => chatMessages.resolve([locale()]).t;\n};\n\nexport const checkChatMessages = () => chatMessages.check();\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,170 @@
1
+ import { fuzzy } from "@k2b/stdlib";
2
+ import { chatCommandKey, foldChatSearchText } from "./chat-behavior";
3
+ const wordPattern = /[^\s\-_.\/·:,()„“"']+/gu;
4
+ const words = text => [...text.matchAll(wordPattern)];
5
+ const prepare = command => {
6
+ const title = command.label ?? command.name;
7
+ // Each folded UTF-16 position maps to the whole original character and its combining marks.
8
+ const starts = [],
9
+ ends = [];
10
+ let foldedTitle = "";
11
+ for (const part of title.matchAll(/\P{M}\p{M}*|\p{M}+/gu)) {
12
+ const folded = foldChatSearchText(part[0]);
13
+ foldedTitle += folded;
14
+ for (let i = 0; i < folded.length; i++) {
15
+ starts.push(part.index);
16
+ ends.push(part.index + part[0].length);
17
+ }
18
+ }
19
+ return {
20
+ foldedTitle,
21
+ starts,
22
+ ends,
23
+ titleWords: words(foldedTitle),
24
+ keywords: [...(command.keywords ?? []), ...(command.label !== undefined ? [command.name] : [])].map(foldChatSearchText),
25
+ app: foldChatSearchText(command.app ?? ""),
26
+ descriptionWords: words(foldChatSearchText(command.description))
27
+ };
28
+ };
29
+ const rankPrepared = (entry, query) => {
30
+ if (!query) return {
31
+ rank: 0
32
+ };
33
+ const titleMatch = (rank, index) => ({
34
+ rank,
35
+ highlight: [entry.starts[index], entry.ends[index + query.length - 1]]
36
+ });
37
+ if (entry.foldedTitle.startsWith(query)) return titleMatch(0, 0);
38
+ const word = entry.titleWords.find(word => word[0].startsWith(query));
39
+ if (word) return titleMatch(1, word.index);
40
+ const index = entry.foldedTitle.indexOf(query);
41
+ if (index >= 0) return titleMatch(2, index);
42
+ if (entry.keywords.some(word => word.startsWith(query))) return {
43
+ rank: 3
44
+ };
45
+ if (entry.app.startsWith(query)) return {
46
+ rank: 4
47
+ };
48
+ if (query.length >= 3 && entry.descriptionWords.some(word => word[0].startsWith(query))) return {
49
+ rank: 5
50
+ };
51
+ if (query.length >= 4) {
52
+ for (const word of [...entry.titleWords.map(word => word[0]), ...entry.keywords]) {
53
+ for (const length of [query.length - 1, query.length, query.length + 1]) {
54
+ if (word.length >= length && fuzzy.distance(query, word.slice(0, length)) <= 1) return {
55
+ rank: 6
56
+ };
57
+ }
58
+ }
59
+ }
60
+ return null;
61
+ };
62
+ export const rankChatCommand = (command, query) => rankPrepared(prepare(command), foldChatSearchText(query).trim());
63
+ export function resolveChatCommandScope(word, scopes) {
64
+ const query = foldChatSearchText(word);
65
+ const folded = scopes.map(scope => ({
66
+ scope,
67
+ words: scope.words.map(foldChatSearchText)
68
+ }));
69
+ const exact = folded.filter(entry => entry.words.includes(query));
70
+ if (exact.length) return exact.length === 1 ? exact[0]?.scope : undefined;
71
+ if (query.length < 3) return undefined;
72
+ let best,
73
+ extension = Infinity;
74
+ for (const entry of folded) {
75
+ const extra = Math.min(...entry.words.filter(word => word.startsWith(query)).map(word => word.length - query.length));
76
+ if (extra < extension) {
77
+ best = entry.scope;
78
+ extension = extra;
79
+ } else if (extra === extension) best = undefined;
80
+ }
81
+ return best;
82
+ }
83
+ export function parseChatCommandQuery(query, scopes) {
84
+ const match = /^(\S+)\s(.*)$/s.exec(query);
85
+ const scope = match ? resolveChatCommandScope(match[1], scopes) : undefined;
86
+ return scope ? {
87
+ scope,
88
+ query: match[2].trimStart()
89
+ } : {
90
+ query
91
+ };
92
+ }
93
+ export function chatCommandListModel(input) {
94
+ const {
95
+ scope,
96
+ query: rawQuery
97
+ } = parseChatCommandQuery(input.query, input.scopes ?? []);
98
+ const query = foldChatSearchText(rawQuery).trim();
99
+ const overview = !scope && !query;
100
+ const groups = scope ? [{
101
+ scopeTitle: scope.title,
102
+ rows: [],
103
+ loading: input.searching,
104
+ placeholder: false
105
+ }] : [...(input.sections ?? []).map(section => ({
106
+ section,
107
+ rows: [],
108
+ loading: Boolean(section.searched && input.searching && !(overview && section.searchOnly)),
109
+ placeholder: false
110
+ })), {
111
+ rows: [],
112
+ loading: !input.sections?.length && input.searching,
113
+ placeholder: false
114
+ }];
115
+ const bySection = new Map(groups.map((group, index) => [group.section?.id, index]));
116
+ const buckets = groups.map(() => []);
117
+ for (const [commands, server] of [[scope ? [] : input.commands, false], [input.searchResults, true]]) {
118
+ for (const command of commands) {
119
+ const index = scope ? 0 : bySection.get(command.section) ?? groups.length - 1;
120
+ buckets[index].push({
121
+ command,
122
+ server
123
+ });
124
+ }
125
+ }
126
+ const seen = new Set();
127
+ for (const [index, group] of groups.entries()) {
128
+ // A search-only section is absent from the overview, rows and placeholder included.
129
+ if (overview && group.section?.searchOnly) continue;
130
+ const ranked = [];
131
+ for (const {
132
+ command,
133
+ server
134
+ } of buckets[index]) {
135
+ const key = chatCommandKey(command);
136
+ if (seen.has(key)) continue;
137
+ if (overview && command.hiddenUntilSearch) continue;
138
+ const match = rankPrepared(prepare(command), query);
139
+ if (!overview && !match && !(server && input.searchResultsFresh)) continue;
140
+ seen.add(key);
141
+ ranked.push({
142
+ command,
143
+ server,
144
+ highlight: match?.highlight,
145
+ rank: match?.rank ?? Infinity
146
+ });
147
+ }
148
+ if (!overview) ranked.sort((a, b) => a.rank - b.rank);
149
+ group.rows = ranked.map(({
150
+ command,
151
+ server,
152
+ highlight
153
+ }) => ({
154
+ command,
155
+ server,
156
+ highlight
157
+ }));
158
+ group.placeholder = group.loading && group.rows.length === 0 && Boolean(scope || !input.searchSettled || buckets[index].some(entry => entry.server));
159
+ }
160
+ // A section without rows has no heading, also in the overview; a scope always shows its title.
161
+ const visible = groups.filter(group => scope || group.rows.length || group.placeholder);
162
+ const rows = visible.flatMap(group => group.rows);
163
+ return {
164
+ scope,
165
+ groups: visible,
166
+ rows,
167
+ empty: rows.length === 0 && !input.searching
168
+ };
169
+ }
170
+ //# sourceMappingURL=command-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["chat/command-search.ts"],"sourcesContent":["import { fuzzy } from \"@k2b/stdlib\";\nimport type { ChatCommand, ChatCommandScope, ChatCommandSection } from \"./ChatComposer\";\nimport { chatCommandKey, foldChatSearchText } from \"./chat-behavior\";\n\nconst wordPattern = /[^\\s\\-_.\\/·:,()„“\"']+/gu;\nconst words = (text: string) => [...text.matchAll(wordPattern)];\ntype Rank = { rank: number; highlight?: readonly [number, number] };\n\nconst prepare = (command: ChatCommand) => {\n const title = command.label ?? command.name;\n // Each folded UTF-16 position maps to the whole original character and its combining marks.\n const starts: number[] = [],\n ends: number[] = [];\n let foldedTitle = \"\";\n for (const part of title.matchAll(/\\P{M}\\p{M}*|\\p{M}+/gu)) {\n const folded = foldChatSearchText(part[0]);\n foldedTitle += folded;\n for (let i = 0; i < folded.length; i++) {\n starts.push(part.index);\n ends.push(part.index + part[0].length);\n }\n }\n return {\n foldedTitle,\n starts,\n ends,\n titleWords: words(foldedTitle),\n keywords: [...(command.keywords ?? []), ...(command.label !== undefined ? [command.name] : [])].map(foldChatSearchText),\n app: foldChatSearchText(command.app ?? \"\"),\n descriptionWords: words(foldChatSearchText(command.description)),\n };\n};\n\nconst rankPrepared = (entry: ReturnType<typeof prepare>, query: string): Rank | null => {\n if (!query) return { rank: 0 };\n const titleMatch = (rank: number, index: number): Rank => ({\n rank,\n highlight: [entry.starts[index]!, entry.ends[index + query.length - 1]!],\n });\n if (entry.foldedTitle.startsWith(query)) return titleMatch(0, 0);\n const word = entry.titleWords.find((word) => word[0].startsWith(query));\n if (word) return titleMatch(1, word.index);\n const index = entry.foldedTitle.indexOf(query);\n if (index >= 0) return titleMatch(2, index);\n if (entry.keywords.some((word) => word.startsWith(query))) return { rank: 3 };\n if (entry.app.startsWith(query)) return { rank: 4 };\n if (query.length >= 3 && entry.descriptionWords.some((word) => word[0].startsWith(query))) return { rank: 5 };\n if (query.length >= 4) {\n for (const word of [...entry.titleWords.map((word) => word[0]), ...entry.keywords]) {\n for (const length of [query.length - 1, query.length, query.length + 1]) {\n if (word.length >= length && fuzzy.distance(query, word.slice(0, length)) <= 1) return { rank: 6 };\n }\n }\n }\n return null;\n};\n\nexport const rankChatCommand = (command: ChatCommand, query: string): Rank | null =>\n rankPrepared(prepare(command), foldChatSearchText(query).trim());\n\nexport function resolveChatCommandScope(word: string, scopes: readonly ChatCommandScope[]): ChatCommandScope | undefined {\n const query = foldChatSearchText(word);\n const folded = scopes.map((scope) => ({ scope, words: scope.words.map(foldChatSearchText) }));\n const exact = folded.filter((entry) => entry.words.includes(query));\n if (exact.length) return exact.length === 1 ? exact[0]?.scope : undefined;\n if (query.length < 3) return undefined;\n let best: ChatCommandScope | undefined,\n extension = Infinity;\n for (const entry of folded) {\n const extra = Math.min(...entry.words.filter((word) => word.startsWith(query)).map((word) => word.length - query.length));\n if (extra < extension) {\n best = entry.scope;\n extension = extra;\n } else if (extra === extension) best = undefined;\n }\n return best;\n}\n\nexport function parseChatCommandQuery(query: string, scopes: readonly ChatCommandScope[]): { scope?: ChatCommandScope; query: string } {\n const match = /^(\\S+)\\s(.*)$/s.exec(query);\n const scope = match ? resolveChatCommandScope(match[1]!, scopes) : undefined;\n return scope ? { scope, query: match![2]!.trimStart() } : { query };\n}\n\nexport type ChatCommandRow = { command: ChatCommand; server: boolean; highlight?: readonly [number, number] };\ntype Group = { section?: ChatCommandSection; scopeTitle?: string; rows: ChatCommandRow[]; loading: boolean; placeholder: boolean };\n\nexport function chatCommandListModel(input: {\n query: string;\n scopes?: readonly ChatCommandScope[];\n sections?: readonly ChatCommandSection[];\n commands: readonly ChatCommand[];\n searchResults: readonly ChatCommand[];\n searchResultsFresh: boolean;\n searchSettled: boolean;\n searching: boolean;\n}): { scope?: ChatCommandScope; groups: Group[]; rows: ChatCommandRow[]; empty: boolean } {\n const { scope, query: rawQuery } = parseChatCommandQuery(input.query, input.scopes ?? []);\n const query = foldChatSearchText(rawQuery).trim();\n const overview = !scope && !query;\n const groups: Group[] = scope\n ? [{ scopeTitle: scope.title, rows: [], loading: input.searching, placeholder: false }]\n : [\n ...(input.sections ?? []).map((section) => ({\n section,\n rows: [],\n loading: Boolean(section.searched && input.searching && !(overview && section.searchOnly)),\n placeholder: false,\n })),\n { rows: [], loading: !input.sections?.length && input.searching, placeholder: false },\n ];\n const bySection = new Map(groups.map((group, index) => [group.section?.id, index]));\n const buckets: { command: ChatCommand; server: boolean }[][] = groups.map(() => []);\n for (const [commands, server] of [\n [scope ? [] : input.commands, false],\n [input.searchResults, true],\n ] as const) {\n for (const command of commands) {\n const index = scope ? 0 : (bySection.get(command.section) ?? groups.length - 1);\n buckets[index]!.push({ command, server });\n }\n }\n const seen = new Set<string>();\n for (const [index, group] of groups.entries()) {\n // A search-only section is absent from the overview, rows and placeholder included.\n if (overview && group.section?.searchOnly) continue;\n const ranked: (ChatCommandRow & { rank: number })[] = [];\n for (const { command, server } of buckets[index]!) {\n const key = chatCommandKey(command);\n if (seen.has(key)) continue;\n if (overview && command.hiddenUntilSearch) continue;\n const match = rankPrepared(prepare(command), query);\n if (!overview && !match && !(server && input.searchResultsFresh)) continue;\n seen.add(key);\n ranked.push({ command, server, highlight: match?.highlight, rank: match?.rank ?? Infinity });\n }\n if (!overview) ranked.sort((a, b) => a.rank - b.rank);\n group.rows = ranked.map(({ command, server, highlight }) => ({ command, server, highlight }));\n group.placeholder =\n group.loading && group.rows.length === 0 && Boolean(scope || !input.searchSettled || buckets[index]!.some((entry) => entry.server));\n }\n // A section without rows has no heading, also in the overview; a scope always shows its title.\n const visible = groups.filter((group) => scope || group.rows.length || group.placeholder);\n const rows = visible.flatMap((group) => group.rows);\n return { scope, groups: visible, rows, empty: rows.length === 0 && !input.searching };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -4,7 +4,7 @@ export function chatCommandQuery(text, caret, selectionEnd = caret) {
4
4
  const before = text.slice(0, caret);
5
5
  if ((before.match(/`/g)?.length ?? 0) % 2) return null;
6
6
  const match = /(?:^|[\s(])\/([^\n/`]*)$/.exec(before);
7
- if (!match) return null;
7
+ if (!match || /^\s/.test(match[1])) return null;
8
8
  return {
9
9
  start: caret - match[1].length - 1,
10
10
  end: caret,
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["chat/composer-document.ts"],"sourcesContent":["import type { ChatMention } from \"./types\";\n\nexport type ChatCommandQuery = { start: number; end: number; query: string };\n\n/** Only the token at the caret; URLs, paths and inline/fenced code are ordinary text. */\nexport function chatCommandQuery(text: string, caret: number, selectionEnd = caret): ChatCommandQuery | null {\n if (caret !== selectionEnd) return null;\n const before = text.slice(0, caret);\n if ((before.match(/`/g)?.length ?? 0) % 2) return null;\n const match = /(?:^|[\\s(])\\/([^\\n/`]*)$/.exec(before);\n if (!match) return null;\n return { start: caret - match[1]!.length - 1, end: caret, query: match[1]! };\n}\n\n/** An edit inside a mention removes its identity; edits around it shift the range. */\nexport function reconcileChatMentions(before: string, after: string, mentions: readonly ChatMention[]): ChatMention[] {\n if (before === after) return [...mentions];\n let start = 0;\n while (start < before.length && start < after.length && before[start] === after[start]) start++;\n let oldEnd = before.length,\n newEnd = after.length;\n while (oldEnd > start && newEnd > start && before[oldEnd - 1] === after[newEnd - 1]) {\n oldEnd--;\n newEnd--;\n }\n return mentions.flatMap((mention) => {\n if (mention.end <= start) return [mention];\n if (mention.start >= oldEnd) return [{ ...mention, start: mention.start + newEnd - oldEnd, end: mention.end + newEnd - oldEnd }];\n return [];\n });\n}\n\nexport function chatMentionSegments(text: string, mentions: readonly ChatMention[]) {\n const segments: { text: string; mention?: ChatMention }[] = [];\n let offset = 0;\n for (const mention of [...mentions].sort((a, b) => a.start - b.start)) {\n if (mention.start < offset || mention.end > text.length || mention.start >= mention.end) continue;\n segments.push({ text: text.slice(offset, mention.start) });\n segments.push({ text: text.slice(mention.start, mention.end), mention });\n offset = mention.end;\n }\n segments.push({ text: text.slice(offset) });\n return segments;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["chat/composer-document.ts"],"sourcesContent":["import type { ChatMention } from \"./types\";\n\nexport type ChatCommandQuery = { start: number; end: number; query: string };\n\n/** Only the token at the caret; URLs, paths and inline/fenced code are ordinary text. */\nexport function chatCommandQuery(text: string, caret: number, selectionEnd = caret): ChatCommandQuery | null {\n if (caret !== selectionEnd) return null;\n const before = text.slice(0, caret);\n if ((before.match(/`/g)?.length ?? 0) % 2) return null;\n const match = /(?:^|[\\s(])\\/([^\\n/`]*)$/.exec(before);\n if (!match || /^\\s/.test(match[1]!)) return null;\n return { start: caret - match[1]!.length - 1, end: caret, query: match[1]! };\n}\n\n/** An edit inside a mention removes its identity; edits around it shift the range. */\nexport function reconcileChatMentions(before: string, after: string, mentions: readonly ChatMention[]): ChatMention[] {\n if (before === after) return [...mentions];\n let start = 0;\n while (start < before.length && start < after.length && before[start] === after[start]) start++;\n let oldEnd = before.length,\n newEnd = after.length;\n while (oldEnd > start && newEnd > start && before[oldEnd - 1] === after[newEnd - 1]) {\n oldEnd--;\n newEnd--;\n }\n return mentions.flatMap((mention) => {\n if (mention.end <= start) return [mention];\n if (mention.start >= oldEnd) return [{ ...mention, start: mention.start + newEnd - oldEnd, end: mention.end + newEnd - oldEnd }];\n return [];\n });\n}\n\nexport function chatMentionSegments(text: string, mentions: readonly ChatMention[]) {\n const segments: { text: string; mention?: ChatMention }[] = [];\n let offset = 0;\n for (const mention of [...mentions].sort((a, b) => a.start - b.start)) {\n if (mention.start < offset || mention.end > text.length || mention.start >= mention.end) continue;\n segments.push({ text: text.slice(offset, mention.start) });\n segments.push({ text: text.slice(mention.start, mention.end), mention });\n offset = mention.end;\n }\n segments.push({ text: text.slice(offset) });\n return segments;\n}\n"],"mappings":"AAIA;AACA,OAAO,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAa,EAAE,YAAY,GAAG,KAAK,EAA2B;EAC3G,IAAI,KAAK,KAAK,YAAY,EAAE,OAAO,IAAI;EACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;EACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,IAAI;EACtD,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC;EACrD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,OAAO,IAAI;EAChD,OAAO;IAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAE,MAAM,GAAG,CAAC;IAAE,GAAG,EAAE,KAAK;IAAE,KAAK,EAAE,KAAK,CAAC,CAAC;EAAG,CAAC;AAC9E;;AAEA;AACA,OAAO,SAAS,qBAAqB,CAAC,MAAc,EAAE,KAAa,EAAE,QAAgC,EAAiB;EACpH,IAAI,MAAM,KAAK,KAAK,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;EAC1C,IAAI,KAAK,GAAG,CAAC;EACb,OAAO,KAAK,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE;EAC/F,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM;IACxB,MAAM,GAAG,KAAK,CAAC,MAAM;EACvB,OAAO,MAAM,GAAG,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;IACnF,MAAM,EAAE;IACR,MAAM,EAAE;EACV;EACA,OAAO,QAAQ,CAAC,OAAO,CAAE,OAAO,IAAK;IACnC,IAAI,OAAO,CAAC,GAAG,IAAI,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC;IAC1C,IAAI,OAAO,CAAC,KAAK,IAAI,MAAM,EAAE,OAAO,CAAC;MAAE,GAAG,OAAO;MAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,MAAM,GAAG,MAAM;MAAE,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,MAAM,GAAG;IAAO,CAAC,CAAC;IAChI,OAAO,EAAE;EACX,CAAC,CAAC;AACJ;AAEA,OAAO,SAAS,mBAAmB,CAAC,IAAY,EAAE,QAAgC,EAAE;EAClF,MAAM,QAAmD,GAAG,EAAE;EAC9D,IAAI,MAAM,GAAG,CAAC;EACd,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE;IACrE,IAAI,OAAO,CAAC,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,EAAE;IACzF,QAAQ,CAAC,IAAI,CAAC;MAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;IAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,IAAI,CAAC;MAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC;MAAE;IAAQ,CAAC,CAAC;IACxE,MAAM,GAAG,OAAO,CAAC,GAAG;EACtB;EACA,QAAQ,CAAC,IAAI,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;EAAE,CAAC,CAAC;EAC3C,OAAO,QAAQ;AACjB","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["chat/index.ts"],"sourcesContent":["export type { ChatContextPanelProps, ChatContextPopupProps } from \"./ChatContextPopup\";\n\nimport { ChatContextPanel, ChatContextPopup } from \"./ChatContextPopup\";\nimport { ChatTasks } from \"./ChatTasks\";\n\nexport type { ChatTask, ChatTasksProps } from \"./ChatTasks\";\n\nimport { ChatComposer } from \"./ChatComposer\";\nimport { ChatActivity, ChatContextUsage, ChatMessage } from \"./ChatPrimitives\";\nimport { ChatRoot } from \"./ChatRoot\";\nimport { ChatTimeline } from \"./ChatTimeline\";\n\nexport type { ChatCommand, ChatCommandContext, ChatComposerProps, ChatFileSelection, ChatPasteHandler } from \"./ChatComposer\";\nexport type { ChatActivityProps, ChatContextUsageProps, ChatMessageProps } from \"./ChatPrimitives\";\nexport { formatChatTokens } from \"./ChatPrimitives\";\nexport type { ChatRootProps } from \"./ChatRoot\";\nexport type { ChatActivityItem, ChatMessageItem, ChatTimelineItem, ChatTimelineProps } from \"./ChatTimeline\";\nexport type {\n ChatAction,\n ChatActivityTone,\n ChatAttachment,\n ChatComposerEmoji,\n ChatComposerMicrophone,\n ChatComposerState,\n ChatContextUsageData,\n ChatDictationState,\n ChatMention,\n ChatMessageStatus,\n ChatModelOption,\n ChatRole,\n ChatSubmitInput,\n ChatSubmitIntent,\n ChatUsage,\n} from \"./types\";\n\n/**\n * Portable, controlled chat surface. Applications own storage, streaming,\n * authorization, rich message rendering, and domain actions.\n */\nexport const Chat = Object.assign(ChatRoot, {\n Tasks: ChatTasks,\n Timeline: ChatTimeline,\n Message: ChatMessage,\n Activity: ChatActivity,\n Composer: ChatComposer,\n ContextUsage: ChatContextUsage,\n ContextPopup: ChatContextPopup,\n ContextPanel: ChatContextPanel,\n});\n\nexport { reconcileChatMentions } from \"./composer-document\";\n"],"mappings":"AAEA,SAAS,gBAAgB,EAAE,gBAAgB,QAAQ,oBAAoB;AACvE,SAAS,SAAS,QAAQ,aAAa;AAIvC,SAAS,YAAY,QAAQ,gBAAgB;AAC7C,SAAS,YAAY,EAAE,gBAAgB,EAAE,WAAW,QAAQ,kBAAkB;AAC9E,SAAS,QAAQ,QAAQ,YAAY;AACrC,SAAS,YAAY,QAAQ,gBAAgB;AAI7C,SAAS,gBAAgB,QAAQ,kBAAkB;AAqBnD;AACA;AACA;AACA;AACA,OAAO,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;EAC1C,KAAK,EAAE,SAAS;EAChB,QAAQ,EAAE,YAAY;EACtB,OAAO,EAAE,WAAW;EACpB,QAAQ,EAAE,YAAY;EACtB,QAAQ,EAAE,YAAY;EACtB,YAAY,EAAE,gBAAgB;EAC9B,YAAY,EAAE,gBAAgB;EAC9B,YAAY,EAAE;AAChB,CAAC,CAAC;AAEF,SAAS,qBAAqB,QAAQ,qBAAqB","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["chat/index.ts"],"sourcesContent":["export type { ChatContextPanelProps, ChatContextPopupProps } from \"./ChatContextPopup\";\n\nimport { ChatContextPanel, ChatContextPopup } from \"./ChatContextPopup\";\nimport { ChatTasks } from \"./ChatTasks\";\n\nexport type { ChatTask, ChatTasksProps } from \"./ChatTasks\";\n\nimport { ChatComposer } from \"./ChatComposer\";\nimport { ChatActivity, ChatContextUsage, ChatMessage } from \"./ChatPrimitives\";\nimport { ChatRoot } from \"./ChatRoot\";\nimport { ChatTimeline } from \"./ChatTimeline\";\n\nexport type {\n ChatCommand,\n ChatCommandContext,\n ChatCommandResult,\n ChatCommandScope,\n ChatCommandSearch,\n ChatCommandSection,\n ChatComposerProps,\n ChatFileSelection,\n ChatPasteHandler,\n} from \"./ChatComposer\";\nexport type { ChatActivityProps, ChatContextUsageProps, ChatMessageProps } from \"./ChatPrimitives\";\nexport { formatChatTokens } from \"./ChatPrimitives\";\nexport type { ChatRootProps } from \"./ChatRoot\";\nexport type { ChatActivityItem, ChatMessageItem, ChatTimelineItem, ChatTimelineProps } from \"./ChatTimeline\";\nexport type {\n ChatAction,\n ChatActivityTone,\n ChatAttachment,\n ChatComposerEmoji,\n ChatComposerMicrophone,\n ChatComposerState,\n ChatContextUsageData,\n ChatDictationState,\n ChatMention,\n ChatMessageStatus,\n ChatModelOption,\n ChatRole,\n ChatSubmitInput,\n ChatSubmitIntent,\n ChatUsage,\n} from \"./types\";\n\n/**\n * Portable, controlled chat surface. Applications own storage, streaming,\n * authorization, rich message rendering, and domain actions.\n */\nexport const Chat = Object.assign(ChatRoot, {\n Tasks: ChatTasks,\n Timeline: ChatTimeline,\n Message: ChatMessage,\n Activity: ChatActivity,\n Composer: ChatComposer,\n ContextUsage: ChatContextUsage,\n ContextPopup: ChatContextPopup,\n ContextPanel: ChatContextPanel,\n});\n\nexport { reconcileChatMentions } from \"./composer-document\";\n"],"mappings":"AAEA,SAAS,gBAAgB,EAAE,gBAAgB,QAAQ,oBAAoB;AACvE,SAAS,SAAS,QAAQ,aAAa;AAIvC,SAAS,YAAY,QAAQ,gBAAgB;AAC7C,SAAS,YAAY,EAAE,gBAAgB,EAAE,WAAW,QAAQ,kBAAkB;AAC9E,SAAS,QAAQ,QAAQ,YAAY;AACrC,SAAS,YAAY,QAAQ,gBAAgB;AAc7C,SAAS,gBAAgB,QAAQ,kBAAkB;AAqBnD;AACA;AACA;AACA;AACA,OAAO,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;EAC1C,KAAK,EAAE,SAAS;EAChB,QAAQ,EAAE,YAAY;EACtB,OAAO,EAAE,WAAW;EACpB,QAAQ,EAAE,YAAY;EACtB,QAAQ,EAAE,YAAY;EACtB,YAAY,EAAE,gBAAgB;EAC9B,YAAY,EAAE,gBAAgB;EAC9B,YAAY,EAAE;AAChB,CAAC,CAAC;AAEF,SAAS,qBAAqB,QAAQ,qBAAqB","ignoreList":[]}
@@ -41,7 +41,6 @@ import { brakedScroll, GESTURE_GAP_MS, SETTLE_RADIUS, settleTarget, stopCrossing
41
41
  import { addDayKeys, DAY_MINUTES, dayGroups, dayHeightTerms, dayKeyRange, dayMinuteAt, dayY, dayYTerms, markerLanes, readDayGeometry, termsCss } from "./calendar-days";
42
42
  import { EventChip, eventHref } from "./calendar-shared";
43
43
  import { AllDayChips, createTimeView, dayBands, dayGaps, daySummary, eventAccent, foldedEntries, gapAside, isNearNow, longDay, markerAbove, nightHours, shortDay, soonNote, TimeBand, TimeCheck, TimeGap, TimeMarker, TimeOverlays, TimeQuickCreate } from "./calendar-time-view";
44
- const groupId = group => group.kind === "day" ? `d:${group.key}` : `e:${group.keys.join(",")}`;
45
44
  const groupFirst = group => group.kind === "day" ? group.key : group.keys[0];
46
45
 
47
46
  /**
@@ -75,8 +74,34 @@ export const CalendarDayView = props => {
75
74
  // Today and the day the reader opened never fold, so a day opened from the month shows its hours to create in.
76
75
  const groups = createMemo(() => dayGroups(keys(), key => view.dayOf(key).empty, new Set([view.nowKey(), view.anchorKey(), ...unfolded()])));
77
76
  const unfold = keys => setUnfolded(value => new Set([...value, ...keys]));
78
- const groupIds = createMemo(() => groups().map(groupId));
79
- const groupsById = createMemo(() => new Map(groups().map(group => [groupId(group), group])));
77
+ let runs = 0;
78
+ // Keep a run's identity so focus on its button survives days joining it at either end.
79
+ const entries = createMemo(previous => {
80
+ const taken = new Set();
81
+ return groups().map(group => {
82
+ if (group.kind === "day") return {
83
+ id: `d:${group.key}`,
84
+ group
85
+ };
86
+ const continued = previous.find(({
87
+ id,
88
+ group: old
89
+ }) => old.kind === "empty" && !taken.has(id) && old.keys.some(key => group.keys.includes(key)));
90
+ const id = continued?.id ?? `e:${++runs}`;
91
+ taken.add(id);
92
+ return {
93
+ id,
94
+ group
95
+ };
96
+ });
97
+ }, []);
98
+ const groupIds = createMemo(() => entries().map(({
99
+ id
100
+ }) => id));
101
+ const groupsById = createMemo(() => new Map(entries().map(({
102
+ id,
103
+ group
104
+ }) => [id, group])));
80
105
 
81
106
  // Where each group starts in the scroll, measured after every change of the groups and on resize.
82
107
  let sections = [];