@lapis-notes/ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/ai-plugin.d.ts +88 -0
  5. package/dist/ai-plugin.js +742 -0
  6. package/dist/bootstrap/build.d.ts +15 -0
  7. package/dist/bootstrap/build.js +28 -0
  8. package/dist/bootstrap/folder-instructions.d.ts +4 -0
  9. package/dist/bootstrap/folder-instructions.js +49 -0
  10. package/dist/bootstrap/format.d.ts +2 -0
  11. package/dist/bootstrap/format.js +46 -0
  12. package/dist/bootstrap/instructions.d.ts +1 -0
  13. package/dist/bootstrap/instructions.js +8 -0
  14. package/dist/bootstrap/types.d.ts +36 -0
  15. package/dist/bootstrap/types.js +3 -0
  16. package/dist/catalog/ai-catalog-panel.svelte +742 -0
  17. package/dist/catalog/ai-catalog-panel.svelte.d.ts +13 -0
  18. package/dist/catalog/ai-catalog-view-type.d.ts +1 -0
  19. package/dist/catalog/ai-catalog-view-type.js +1 -0
  20. package/dist/catalog/ai-catalog-view.d.ts +22 -0
  21. package/dist/catalog/ai-catalog-view.js +48 -0
  22. package/dist/catalog/filter.d.ts +3 -0
  23. package/dist/catalog/filter.js +24 -0
  24. package/dist/catalog/inventory.d.ts +21 -0
  25. package/dist/catalog/inventory.js +255 -0
  26. package/dist/catalog/tree.d.ts +12 -0
  27. package/dist/catalog/tree.js +60 -0
  28. package/dist/catalog/types.d.ts +44 -0
  29. package/dist/catalog/types.js +1 -0
  30. package/dist/chat/ai-approval-card.svelte +54 -0
  31. package/dist/chat/ai-approval-card.svelte.d.ts +9 -0
  32. package/dist/chat/ai-chat-panel.svelte +1288 -0
  33. package/dist/chat/ai-chat-panel.svelte.d.ts +50 -0
  34. package/dist/chat/ai-inventory-result.css +55 -0
  35. package/dist/chat/ai-inventory-result.svelte +63 -0
  36. package/dist/chat/ai-inventory-result.svelte.d.ts +5 -0
  37. package/dist/chat/ai-question-card.svelte +132 -0
  38. package/dist/chat/ai-question-card.svelte.d.ts +9 -0
  39. package/dist/chat/ai-view-bootstrap.d.ts +13 -0
  40. package/dist/chat/ai-view-bootstrap.js +86 -0
  41. package/dist/chat/ai-view-panel.svelte +89 -0
  42. package/dist/chat/ai-view-panel.svelte.d.ts +13 -0
  43. package/dist/chat/ai-view-type.d.ts +1 -0
  44. package/dist/chat/ai-view-type.js +1 -0
  45. package/dist/chat/ai-view.d.ts +60 -0
  46. package/dist/chat/ai-view.js +121 -0
  47. package/dist/chat/chat-controller.svelte.d.ts +82 -0
  48. package/dist/chat/chat-controller.svelte.js +1926 -0
  49. package/dist/chat/chat-items.d.ts +88 -0
  50. package/dist/chat/chat-items.js +9 -0
  51. package/dist/chat/chat-mentions.d.ts +15 -0
  52. package/dist/chat/chat-mentions.js +77 -0
  53. package/dist/chat/chat-result-views.d.ts +5 -0
  54. package/dist/chat/chat-result-views.js +15 -0
  55. package/dist/chat/chat-session.d.ts +28 -0
  56. package/dist/chat/chat-session.js +50 -0
  57. package/dist/chat/chat-status.d.ts +1 -0
  58. package/dist/chat/chat-status.js +3 -0
  59. package/dist/chat/chat-time.d.ts +18 -0
  60. package/dist/chat/chat-time.js +90 -0
  61. package/dist/chat/chat-tool-display.d.ts +17 -0
  62. package/dist/chat/chat-tool-display.js +213 -0
  63. package/dist/chat/chat-tool-identity.d.ts +4 -0
  64. package/dist/chat/chat-tool-identity.js +42 -0
  65. package/dist/chat/chat-trace.d.ts +6 -0
  66. package/dist/chat/chat-trace.js +256 -0
  67. package/dist/chat/live-runtime-unavailable.d.ts +1 -0
  68. package/dist/chat/live-runtime-unavailable.js +1 -0
  69. package/dist/commands/agent.d.ts +8 -0
  70. package/dist/commands/agent.js +38 -0
  71. package/dist/commands/catalog.d.ts +18 -0
  72. package/dist/commands/catalog.js +115 -0
  73. package/dist/commands/discovery.d.ts +36 -0
  74. package/dist/commands/discovery.js +151 -0
  75. package/dist/commands/groups.d.ts +16 -0
  76. package/dist/commands/groups.js +121 -0
  77. package/dist/commands/inspect.d.ts +18 -0
  78. package/dist/commands/inspect.js +29 -0
  79. package/dist/commands/markdown.d.ts +21 -0
  80. package/dist/commands/markdown.js +117 -0
  81. package/dist/commands/parser.d.ts +4 -0
  82. package/dist/commands/parser.js +24 -0
  83. package/dist/commands/reserved.d.ts +4 -0
  84. package/dist/commands/reserved.js +107 -0
  85. package/dist/commands/router.d.ts +17 -0
  86. package/dist/commands/router.js +170 -0
  87. package/dist/commands/seed.d.ts +4 -0
  88. package/dist/commands/seed.js +8 -0
  89. package/dist/commands/types.d.ts +54 -0
  90. package/dist/commands/types.js +1 -0
  91. package/dist/commands/user-agents-node.d.ts +3 -0
  92. package/dist/commands/user-agents-node.js +52 -0
  93. package/dist/commands/user-agents.d.ts +22 -0
  94. package/dist/commands/user-agents.js +40 -0
  95. package/dist/conversations/approval-grants.d.ts +20 -0
  96. package/dist/conversations/approval-grants.js +75 -0
  97. package/dist/conversations/binding-state.d.ts +6 -0
  98. package/dist/conversations/binding-state.js +35 -0
  99. package/dist/conversations/context-handoff.d.ts +25 -0
  100. package/dist/conversations/context-handoff.js +207 -0
  101. package/dist/conversations/conversation-index-coordinator.d.ts +18 -0
  102. package/dist/conversations/conversation-index-coordinator.js +81 -0
  103. package/dist/conversations/conversation-index.d.ts +16 -0
  104. package/dist/conversations/conversation-index.js +125 -0
  105. package/dist/conversations/conversation-locator.d.ts +2 -0
  106. package/dist/conversations/conversation-locator.js +20 -0
  107. package/dist/conversations/conversation-palette.d.ts +18 -0
  108. package/dist/conversations/conversation-palette.js +29 -0
  109. package/dist/conversations/conversation-repository.d.ts +39 -0
  110. package/dist/conversations/conversation-repository.js +213 -0
  111. package/dist/conversations/handoff-summary.d.ts +48 -0
  112. package/dist/conversations/handoff-summary.js +269 -0
  113. package/dist/conversations/hashes.d.ts +5 -0
  114. package/dist/conversations/hashes.js +40 -0
  115. package/dist/conversations/history-creation-scope.d.ts +12 -0
  116. package/dist/conversations/history-creation-scope.js +44 -0
  117. package/dist/conversations/jsonl.d.ts +10 -0
  118. package/dist/conversations/jsonl.js +41 -0
  119. package/dist/conversations/memory-transcript-store.d.ts +15 -0
  120. package/dist/conversations/memory-transcript-store.js +119 -0
  121. package/dist/conversations/paths.d.ts +14 -0
  122. package/dist/conversations/paths.js +92 -0
  123. package/dist/conversations/redaction.d.ts +13 -0
  124. package/dist/conversations/redaction.js +94 -0
  125. package/dist/conversations/reveal-scope.d.ts +3 -0
  126. package/dist/conversations/reveal-scope.js +8 -0
  127. package/dist/conversations/scope-resolver.d.ts +11 -0
  128. package/dist/conversations/scope-resolver.js +18 -0
  129. package/dist/conversations/scope-tree.d.ts +10 -0
  130. package/dist/conversations/scope-tree.js +71 -0
  131. package/dist/conversations/transcript-projection.d.ts +9 -0
  132. package/dist/conversations/transcript-projection.js +409 -0
  133. package/dist/conversations/transcript-store.d.ts +24 -0
  134. package/dist/conversations/transcript-store.js +27 -0
  135. package/dist/conversations/types.d.ts +224 -0
  136. package/dist/conversations/types.js +12 -0
  137. package/dist/conversations/validation.d.ts +4 -0
  138. package/dist/conversations/validation.js +383 -0
  139. package/dist/conversations/vault-transcript-store.d.ts +22 -0
  140. package/dist/conversations/vault-transcript-store.js +219 -0
  141. package/dist/core/event-queue.d.ts +6 -0
  142. package/dist/core/event-queue.js +39 -0
  143. package/dist/core/types.d.ts +241 -0
  144. package/dist/core/types.js +50 -0
  145. package/dist/history/ai-history-panel.svelte +937 -0
  146. package/dist/history/ai-history-panel.svelte.d.ts +17 -0
  147. package/dist/history/ai-history-view-type.d.ts +1 -0
  148. package/dist/history/ai-history-view-type.js +1 -0
  149. package/dist/history/ai-history-view.d.ts +26 -0
  150. package/dist/history/ai-history-view.js +51 -0
  151. package/dist/history/conversation-history-tree.d.ts +15 -0
  152. package/dist/history/conversation-history-tree.js +71 -0
  153. package/dist/host/agent-workspace.d.ts +6 -0
  154. package/dist/host/agent-workspace.js +16 -0
  155. package/dist/host/create-host-runtimes.d.ts +2 -0
  156. package/dist/host/create-host-runtimes.js +14 -0
  157. package/dist/host/desktop-process-host.d.ts +6 -0
  158. package/dist/host/desktop-process-host.js +74 -0
  159. package/dist/host/host-runtime-availability.d.ts +1 -0
  160. package/dist/host/host-runtime-availability.js +3 -0
  161. package/dist/host/process-host.d.ts +31 -0
  162. package/dist/host/process-host.js +6 -0
  163. package/dist/index.d.ts +69 -0
  164. package/dist/index.js +49 -0
  165. package/dist/jsonl/ai-jsonl-preview.d.ts +27 -0
  166. package/dist/jsonl/ai-jsonl-preview.js +68 -0
  167. package/dist/jsonl/ai-jsonl-view-type.d.ts +1 -0
  168. package/dist/jsonl/ai-jsonl-view-type.js +1 -0
  169. package/dist/jsonl/ai-jsonl-view.d.ts +16 -0
  170. package/dist/jsonl/ai-jsonl-view.js +57 -0
  171. package/dist/jsonl/ai-jsonl-view.svelte +260 -0
  172. package/dist/jsonl/ai-jsonl-view.svelte.d.ts +9 -0
  173. package/dist/memory/candidate-extractor.d.ts +25 -0
  174. package/dist/memory/candidate-extractor.js +191 -0
  175. package/dist/memory/forget-confirmation.d.ts +3 -0
  176. package/dist/memory/forget-confirmation.js +45 -0
  177. package/dist/memory/hashes.d.ts +5 -0
  178. package/dist/memory/hashes.js +37 -0
  179. package/dist/memory/memory-ingestion-coordinator.d.ts +19 -0
  180. package/dist/memory/memory-ingestion-coordinator.js +108 -0
  181. package/dist/memory/memory-record-store.d.ts +62 -0
  182. package/dist/memory/memory-record-store.js +369 -0
  183. package/dist/memory/memory-scheduler.d.ts +21 -0
  184. package/dist/memory/memory-scheduler.js +143 -0
  185. package/dist/memory/memory-tools.d.ts +61 -0
  186. package/dist/memory/memory-tools.js +99 -0
  187. package/dist/memory/native-memory-service.d.ts +72 -0
  188. package/dist/memory/native-memory-service.js +1256 -0
  189. package/dist/memory/paths.d.ts +13 -0
  190. package/dist/memory/paths.js +60 -0
  191. package/dist/memory/runtime-consolidation-provider.d.ts +18 -0
  192. package/dist/memory/runtime-consolidation-provider.js +154 -0
  193. package/dist/memory/types.d.ts +139 -0
  194. package/dist/memory/types.js +1 -0
  195. package/dist/providers/acp-model-provider.d.ts +11 -0
  196. package/dist/providers/acp-model-provider.js +99 -0
  197. package/dist/providers/codex-model-catalog.d.ts +7 -0
  198. package/dist/providers/codex-model-catalog.js +179 -0
  199. package/dist/providers/codex-model-provider.d.ts +12 -0
  200. package/dist/providers/codex-model-provider.js +42 -0
  201. package/dist/providers/model-provider.d.ts +26 -0
  202. package/dist/providers/model-provider.js +26 -0
  203. package/dist/public-components.d.ts +3 -0
  204. package/dist/public-components.js +3 -0
  205. package/dist/registry/runtime-registry.d.ts +11 -0
  206. package/dist/registry/runtime-registry.js +71 -0
  207. package/dist/registry/select-runtime.d.ts +9 -0
  208. package/dist/registry/select-runtime.js +31 -0
  209. package/dist/runtime-adapters.d.ts +16 -0
  210. package/dist/runtime-adapters.js +9 -0
  211. package/dist/runtimes/acp/acp-event-mapper.d.ts +56 -0
  212. package/dist/runtimes/acp/acp-event-mapper.js +237 -0
  213. package/dist/runtimes/acp/acp-runtime.d.ts +46 -0
  214. package/dist/runtimes/acp/acp-runtime.js +165 -0
  215. package/dist/runtimes/acp/desktop-acp-backend.d.ts +16 -0
  216. package/dist/runtimes/acp/desktop-acp-backend.js +243 -0
  217. package/dist/runtimes/codex/app-server-protocol.d.ts +28 -0
  218. package/dist/runtimes/codex/app-server-protocol.js +274 -0
  219. package/dist/runtimes/codex/codex-runtime.d.ts +24 -0
  220. package/dist/runtimes/codex/codex-runtime.js +318 -0
  221. package/dist/runtimes/fake/fake-runtime.d.ts +51 -0
  222. package/dist/runtimes/fake/fake-runtime.js +257 -0
  223. package/dist/sessions/plugin-data.d.ts +8 -0
  224. package/dist/sessions/plugin-data.js +29 -0
  225. package/dist/sessions/session-store.d.ts +47 -0
  226. package/dist/sessions/session-store.js +117 -0
  227. package/dist/settings/acp-agents.d.ts +8 -0
  228. package/dist/settings/acp-agents.js +11 -0
  229. package/dist/settings/ai-settings-tab.d.ts +8 -0
  230. package/dist/settings/ai-settings-tab.js +249 -0
  231. package/dist/settings/ai-settings.d.ts +43 -0
  232. package/dist/settings/ai-settings.js +147 -0
  233. package/dist/settings/app-tool-setting-rows.d.ts +16 -0
  234. package/dist/settings/app-tool-setting-rows.js +30 -0
  235. package/dist/settings/register-ai-settings.d.ts +3 -0
  236. package/dist/settings/register-ai-settings.js +393 -0
  237. package/dist/skills/activation.d.ts +2 -0
  238. package/dist/skills/activation.js +20 -0
  239. package/dist/skills/bundled/lapis-notes.d.ts +3 -0
  240. package/dist/skills/bundled/lapis-notes.js +52 -0
  241. package/dist/skills/bundled/research.d.ts +6 -0
  242. package/dist/skills/bundled/research.js +29 -0
  243. package/dist/skills/discovery.d.ts +21 -0
  244. package/dist/skills/discovery.js +227 -0
  245. package/dist/skills/manifest.d.ts +3 -0
  246. package/dist/skills/manifest.js +39 -0
  247. package/dist/skills/parser.d.ts +12 -0
  248. package/dist/skills/parser.js +124 -0
  249. package/dist/skills/registry.d.ts +21 -0
  250. package/dist/skills/registry.js +89 -0
  251. package/dist/skills/seed.d.ts +5 -0
  252. package/dist/skills/seed.js +18 -0
  253. package/dist/skills/skill-tools.d.ts +8 -0
  254. package/dist/skills/skill-tools.js +113 -0
  255. package/dist/skills/types.d.ts +67 -0
  256. package/dist/skills/types.js +1 -0
  257. package/dist/styles.css +514 -0
  258. package/dist/tools/app-tool-host.d.ts +47 -0
  259. package/dist/tools/app-tool-host.js +419 -0
  260. package/dist/tools/desktop-app-tool-bridge.d.ts +23 -0
  261. package/dist/tools/desktop-app-tool-bridge.js +223 -0
  262. package/dist/tools/mcp-server-registry.d.ts +7 -0
  263. package/dist/tools/mcp-server-registry.js +30 -0
  264. package/manifest.json +34 -0
  265. package/package.json +83 -0
  266. package/styles.css +1 -0
@@ -0,0 +1,937 @@
1
+ <script lang="ts">
2
+ import { useTextHighlight, type App } from "@lapis-notes/api";
3
+ import { SearchFilterBar } from "@lapismd/design-core/filter";
4
+ import { Badge } from "@lapismd/design-core/shadcn/badge";
5
+ import { Button } from "@lapismd/design-core/shadcn/button";
6
+ import * as Collapsible from "@lapismd/design-core/shadcn/collapsible";
7
+ import * as DropdownMenu from "@lapismd/design-core/shadcn/dropdown-menu";
8
+ import { ScrollArea } from "@lapismd/design-core/shadcn/scroll-area";
9
+ import * as Sidebar from "@lapismd/design-core/shadcn/sidebar";
10
+ import { Switch } from "@lapismd/design-core/shadcn/switch";
11
+ import * as Tooltip from "@lapismd/design-core/shadcn/tooltip";
12
+ import ArchiveIcon from "@lucide/svelte/icons/archive";
13
+ import ArchiveRestoreIcon from "@lucide/svelte/icons/archive-restore";
14
+ import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
15
+ import ChevronsUpDownIcon from "@lucide/svelte/icons/chevrons-up-down";
16
+ import FolderClosedIcon from "@lucide/svelte/icons/folder-closed";
17
+ import FolderOpenIcon from "@lucide/svelte/icons/folder-open";
18
+ import MessageSquareIcon from "@lucide/svelte/icons/message-square";
19
+ import MoreHorizontalIcon from "@lucide/svelte/icons/ellipsis";
20
+ import PlusIcon from "@lucide/svelte/icons/plus";
21
+ import TrashIcon from "@lucide/svelte/icons/trash-2";
22
+ import { onMount, tick } from "svelte";
23
+ import { formatChatTimestamp } from "../chat/chat-time";
24
+ import type { ConversationRepository } from "../conversations/conversation-repository";
25
+ import type { ConversationListEntry } from "../conversations/transcript-store";
26
+ import type { ConversationLocation } from "../conversations/types";
27
+ import {
28
+ FILE_EXPLORER_SELECTION_CHANGE_EVENT,
29
+ conversationScopeFromVaultPath,
30
+ resolveHistoryCreationScope,
31
+ workspaceEvents,
32
+ } from "../conversations/history-creation-scope";
33
+ import { formatDirectoryContextLabel } from "../conversations/scope-tree";
34
+ import {
35
+ buildConversationHistoryTree,
36
+ conversationHistoryFolderPaths,
37
+ type ConversationHistoryFolder,
38
+ } from "./conversation-history-tree";
39
+
40
+ let {
41
+ app,
42
+ repository,
43
+ getScope,
44
+ getActiveConversation,
45
+ onOpenConversation,
46
+ onNewConversation,
47
+ listConversationFolders: _listConversationFolders,
48
+ searchAllConversations,
49
+ }: {
50
+ app: App;
51
+ repository: ConversationRepository;
52
+ getScope: () => string;
53
+ getActiveConversation: () => ConversationLocation | null;
54
+ onOpenConversation: (
55
+ location: ConversationLocation,
56
+ ) => void | Promise<void>;
57
+ onNewConversation: (scopeDir: string) => void | Promise<void>;
58
+ listConversationFolders: () => string[];
59
+ searchAllConversations: (query: string) => Promise<ConversationListEntry[]>;
60
+ } = $props();
61
+
62
+ let root = $state<HTMLDivElement>();
63
+ let scopeDir = $state("");
64
+ let selectedScope = $state("");
65
+ let activeConversation = $state<ConversationLocation | null>(null);
66
+ let entries = $state<ConversationListEntry[]>([]);
67
+ let showArchived = $state(false);
68
+ let filtersExpanded = $state(false);
69
+ let loading = $state(false);
70
+ let error = $state<string | null>(null);
71
+ let query = $state("");
72
+ let openScopes = $state<Set<string>>(new Set());
73
+ let selectionInitialized = false;
74
+ let contextLockedByTree = false;
75
+ let refreshVersion = 0;
76
+
77
+ const visibleEntries = $derived(
78
+ entries.filter(
79
+ (entry) => showArchived || entry.metadata?.status !== "archived",
80
+ ),
81
+ );
82
+ const folders = $derived(buildConversationHistoryTree(visibleEntries));
83
+ const folderPaths = $derived(conversationHistoryFolderPaths(folders));
84
+ const allFoldersOpen = $derived(
85
+ folderPaths.length > 0 && folderPaths.every((path) => openScopes.has(path)),
86
+ );
87
+ const creationScopeLabel = $derived(
88
+ formatDirectoryContextLabel(selectedScope),
89
+ );
90
+ const expandFoldersLabel = $derived(
91
+ allFoldersOpen
92
+ ? "Collapse all conversation folders"
93
+ : "Expand all conversation folders",
94
+ );
95
+ const newChatLabel = $derived(`New chat in ${creationScopeLabel}`);
96
+
97
+ function entryKey(entry: ConversationListEntry): string {
98
+ return `${entry.location.scopeDir}\u0000${entry.location.conversationId}`;
99
+ }
100
+
101
+ function sameLocation(
102
+ left: ConversationLocation | null,
103
+ right: ConversationLocation,
104
+ ): boolean {
105
+ return (
106
+ left?.scopeDir === right.scopeDir &&
107
+ left.conversationId === right.conversationId
108
+ );
109
+ }
110
+
111
+ function revealScope(path: string): void {
112
+ const next = new Set(openScopes);
113
+ let changed = false;
114
+ const add = (scope: string) => {
115
+ if (next.has(scope)) return;
116
+ next.add(scope);
117
+ changed = true;
118
+ };
119
+ if (!path) add("");
120
+ const segments = path.split("/").filter(Boolean);
121
+ for (let index = 0; index < segments.length; index += 1) {
122
+ add(segments.slice(0, index + 1).join("/"));
123
+ }
124
+ if (changed) openScopes = next;
125
+ }
126
+
127
+ function setFolderOpen(path: string, open: boolean): void {
128
+ const next = new Set(openScopes);
129
+ if (open) next.add(path);
130
+ else next.delete(path);
131
+ openScopes = next;
132
+ }
133
+
134
+ function selectFolder(path: string): void {
135
+ selectedScope = path;
136
+ contextLockedByTree = true;
137
+ }
138
+
139
+ function applyCreationScope(path: string, source: "workspace" | "tree"): void {
140
+ if (selectedScope !== path) selectedScope = path;
141
+ contextLockedByTree = source === "tree";
142
+ revealScope(path);
143
+ }
144
+
145
+ function toggleAllFolders(): void {
146
+ openScopes = allFoldersOpen ? new Set() : new Set(folderPaths);
147
+ }
148
+
149
+ function matchesFolderLabel(
150
+ entry: ConversationListEntry,
151
+ normalizedQuery: string,
152
+ ): boolean {
153
+ const scope = entry.location.scopeDir.toLocaleLowerCase();
154
+ const title = entry.metadata?.title?.toLocaleLowerCase() ?? "";
155
+ return scope.includes(normalizedQuery) || title.includes(normalizedQuery);
156
+ }
157
+
158
+ async function matchLocalConversation(
159
+ entry: ConversationListEntry,
160
+ normalizedQuery: string,
161
+ ): Promise<ConversationListEntry | null> {
162
+ if (matchesFolderLabel(entry, normalizedQuery)) return entry;
163
+ if (entry.unavailableReason) return null;
164
+ try {
165
+ const snapshot = await repository.read(entry.location);
166
+ const searchable = snapshot.transcript.flatMap((item): string[] => {
167
+ if (item.type === "message" || item.type === "thinking.summary") {
168
+ return [item.text];
169
+ }
170
+ if (item.type === "tool") {
171
+ return [item.name, ...(item.input ? [item.input] : [])];
172
+ }
173
+ return [];
174
+ });
175
+ const preview = searchable.find((value) =>
176
+ value.toLocaleLowerCase().includes(normalizedQuery),
177
+ );
178
+ return preview ? { ...entry, preview } : null;
179
+ } catch {
180
+ return null;
181
+ }
182
+ }
183
+
184
+ function mergeLocalEntries(
185
+ indexedEntries: ConversationListEntry[],
186
+ localEntries: ConversationListEntry[],
187
+ ): ConversationListEntry[] {
188
+ const localKeys = new Set(localEntries.map((entry) => entryKey(entry)));
189
+ const merged = new Map(
190
+ indexedEntries
191
+ .filter((entry) => localKeys.has(entryKey(entry)))
192
+ .map((entry) => [entryKey(entry), entry]),
193
+ );
194
+ for (const entry of localEntries) {
195
+ const indexed = merged.get(entryKey(entry));
196
+ merged.set(entryKey(entry), {
197
+ ...indexed,
198
+ ...entry,
199
+ ...(indexed?.preview ? { preview: indexed.preview } : {}),
200
+ });
201
+ }
202
+ return [...merged.values()];
203
+ }
204
+
205
+ async function loadEntries(): Promise<ConversationListEntry[]> {
206
+ const localEntries = await repository.listAll();
207
+ const trimmed = query.trim();
208
+ if (!trimmed) {
209
+ try {
210
+ return mergeLocalEntries(
211
+ await searchAllConversations(""),
212
+ localEntries,
213
+ );
214
+ } catch {
215
+ return localEntries;
216
+ }
217
+ }
218
+
219
+ const normalizedQuery = trimmed.toLocaleLowerCase();
220
+ const localMatches = (
221
+ await Promise.all(
222
+ localEntries.map((entry) =>
223
+ matchLocalConversation(entry, normalizedQuery),
224
+ ),
225
+ )
226
+ ).filter((entry): entry is ConversationListEntry => Boolean(entry));
227
+ let matches: ConversationListEntry[];
228
+ let allEntries: ConversationListEntry[];
229
+ try {
230
+ [matches, allEntries] = await Promise.all([
231
+ searchAllConversations(trimmed),
232
+ searchAllConversations(""),
233
+ ]);
234
+ } catch {
235
+ return localMatches;
236
+ }
237
+ const merged = new Map(matches.map((entry) => [entryKey(entry), entry]));
238
+ for (const entry of allEntries) {
239
+ if (
240
+ matchesFolderLabel(entry, normalizedQuery) &&
241
+ !merged.has(entryKey(entry))
242
+ ) {
243
+ merged.set(entryKey(entry), entry);
244
+ }
245
+ }
246
+ return mergeLocalEntries([...merged.values()], localMatches);
247
+ }
248
+
249
+ async function refresh(followActiveScope = false): Promise<void> {
250
+ const version = ++refreshVersion;
251
+ const nextScope = getScope();
252
+ if (scopeDir !== nextScope) scopeDir = nextScope;
253
+ const nextActiveConversation = getActiveConversation();
254
+ if (
255
+ activeConversation?.scopeDir !== nextActiveConversation?.scopeDir ||
256
+ activeConversation?.conversationId !==
257
+ nextActiveConversation?.conversationId
258
+ ) {
259
+ activeConversation = nextActiveConversation;
260
+ }
261
+ if (followActiveScope || !selectionInitialized) {
262
+ if (!contextLockedByTree) {
263
+ applyCreationScope(
264
+ resolveHistoryCreationScope(app, nextScope),
265
+ "workspace",
266
+ );
267
+ } else {
268
+ revealScope(selectedScope);
269
+ }
270
+ selectionInitialized = true;
271
+ }
272
+ if (!loading) loading = true;
273
+ if (error !== null) error = null;
274
+ try {
275
+ const next = await loadEntries();
276
+ if (version === refreshVersion) entries = next;
277
+ } catch (cause) {
278
+ if (version === refreshVersion) {
279
+ error = cause instanceof Error ? cause.message : String(cause);
280
+ }
281
+ } finally {
282
+ if (version === refreshVersion) loading = false;
283
+ }
284
+ }
285
+
286
+ async function setArchived(
287
+ entry: ConversationListEntry,
288
+ archived: boolean,
289
+ ): Promise<void> {
290
+ await repository.archive(entry.location, archived);
291
+ await refresh();
292
+ }
293
+
294
+ async function remove(entry: ConversationListEntry): Promise<void> {
295
+ await repository.delete(entry.location);
296
+ await refresh();
297
+ }
298
+
299
+ function updateQuery(value: string): void {
300
+ query = value;
301
+ void refresh();
302
+ }
303
+
304
+ $effect(() => {
305
+ scopeDir;
306
+ entries;
307
+ void tick().then(() => {
308
+ root
309
+ ?.querySelector<HTMLElement>('[data-active-folder="true"]')
310
+ ?.scrollIntoView({ block: "nearest", inline: "nearest" });
311
+ });
312
+ });
313
+
314
+ onMount(() => {
315
+ const refreshFromWorkspace = () => {
316
+ queueMicrotask(() => void refresh(true));
317
+ };
318
+ const refreshFromVault = () => void refresh();
319
+ const applyExplorerSelection = (path: unknown) => {
320
+ if (typeof path !== "string") return;
321
+ applyCreationScope(conversationScopeFromVaultPath(app, path), "workspace");
322
+ };
323
+ const events = workspaceEvents(app);
324
+ const activeLeaf = app.workspace.on(
325
+ "active-leaf-change",
326
+ refreshFromWorkspace,
327
+ );
328
+ const explorerSelection = events.on(
329
+ FILE_EXPLORER_SELECTION_CHANGE_EVENT,
330
+ applyExplorerSelection,
331
+ );
332
+ const created = app.vault.on("create", refreshFromVault);
333
+ const deleted = app.vault.on("delete", refreshFromVault);
334
+ const modified = app.vault.on("modify", refreshFromVault);
335
+ const renamed = app.vault.on("rename", refreshFromVault);
336
+ void refresh(true);
337
+ return () => {
338
+ app.workspace.offref(activeLeaf);
339
+ events.offref(explorerSelection);
340
+ app.vault.offref(created);
341
+ app.vault.offref(deleted);
342
+ app.vault.offref(modified);
343
+ app.vault.offref(renamed);
344
+ };
345
+ });
346
+ </script>
347
+
348
+ <Tooltip.Provider delayDuration={0}>
349
+ <div
350
+ bind:this={root}
351
+ class="ai-history"
352
+ data-ui-component="ai-conversation-history"
353
+ data-testid="ai-conversation-history"
354
+ data-current-scope={scopeDir || "vault-root"}
355
+ data-creation-scope={selectedScope || "vault-root"}
356
+ >
357
+ <div class="ai-history__chrome" data-ui-part="chrome">
358
+ <SearchFilterBar
359
+ value={query}
360
+ inputMode="plain"
361
+ ariaLabel="Search conversations"
362
+ placeholder="Search conversations…"
363
+ showFilterToggle
364
+ bind:filtersExpanded
365
+ expandFiltersLabel="Show conversation options"
366
+ collapseFiltersLabel="Hide conversation options"
367
+ onValueChange={updateQuery}
368
+ onClearSearch={() => updateQuery("")}
369
+ >
370
+ {#snippet filters()}
371
+ <label class="ai-history__archive-filter">
372
+ <Switch
373
+ size="sm"
374
+ aria-label="Show archived conversations"
375
+ bind:checked={showArchived}
376
+ />
377
+ <span>Show archived</span>
378
+ </label>
379
+ {/snippet}
380
+ {#snippet actions()}
381
+ <Tooltip.Root>
382
+ <Tooltip.Trigger>
383
+ {#snippet child({ props }: { props: Record<string, unknown> })}
384
+ <Button
385
+ {...props}
386
+ size="icon-sm"
387
+ variant="ghost"
388
+ aria-label={expandFoldersLabel}
389
+ disabled={folderPaths.length === 0}
390
+ onclick={toggleAllFolders}
391
+ >
392
+ <ChevronsUpDownIcon
393
+ data-icon="inline-start"
394
+ aria-hidden="true"
395
+ />
396
+ </Button>
397
+ {/snippet}
398
+ </Tooltip.Trigger>
399
+ <Tooltip.Content side="bottom">{expandFoldersLabel}</Tooltip.Content>
400
+ </Tooltip.Root>
401
+ <Tooltip.Root>
402
+ <Tooltip.Trigger>
403
+ {#snippet child({ props }: { props: Record<string, unknown> })}
404
+ <Button
405
+ {...props}
406
+ size="icon-sm"
407
+ variant="ghost"
408
+ aria-label={newChatLabel}
409
+ onclick={() => void onNewConversation(selectedScope)}
410
+ >
411
+ <PlusIcon data-icon="inline-start" aria-hidden="true" />
412
+ </Button>
413
+ {/snippet}
414
+ </Tooltip.Trigger>
415
+ <Tooltip.Content side="bottom">{newChatLabel}</Tooltip.Content>
416
+ </Tooltip.Root>
417
+ {/snippet}
418
+ </SearchFilterBar>
419
+ <p
420
+ class="ai-history__creation-scope"
421
+ data-testid="ai-history-creation-scope"
422
+ aria-hidden="true"
423
+ >
424
+ {creationScopeLabel}
425
+ </p>
426
+ </div>
427
+
428
+ <ScrollArea class="ai-history__scroll">
429
+ <div class="ai-history__body">
430
+ {#if error}
431
+ <p class="ai-history__state" role="alert">{error}</p>
432
+ {:else if loading && entries.length === 0}
433
+ <p class="ai-history__state">Loading conversations…</p>
434
+ {:else if folders.length === 0}
435
+ <p class="ai-history__state">
436
+ {query.trim()
437
+ ? "No conversations match your search."
438
+ : showArchived
439
+ ? "No conversations yet."
440
+ : "No active conversations yet."}
441
+ </p>
442
+ {:else}
443
+ <Sidebar.Menu
444
+ class="ai-history__tree"
445
+ role="tree"
446
+ aria-label="Conversation history"
447
+ >
448
+ {#each folders as folder (folder.path || "vault-root")}
449
+ {@render FolderNode({ folder })}
450
+ {/each}
451
+ </Sidebar.Menu>
452
+ {/if}
453
+ </div>
454
+ </ScrollArea>
455
+ </div>
456
+ </Tooltip.Provider>
457
+
458
+ {#snippet FolderNode({
459
+ folder,
460
+ level = 1,
461
+ nested = false,
462
+ }: {
463
+ folder: ConversationHistoryFolder;
464
+ level?: number;
465
+ nested?: boolean;
466
+ })}
467
+ {@const Item = nested ? Sidebar.MenuSubItem : Sidebar.MenuItem}
468
+ {@const open = query.trim() ? true : openScopes.has(folder.path)}
469
+ <Item role="none" class="ai-history__folder-item">
470
+ <Collapsible.Root
471
+ {open}
472
+ onOpenChange={(value) => setFolderOpen(folder.path, value)}
473
+ >
474
+ <Collapsible.Trigger
475
+ class="ai-history__folder-row"
476
+ role="treeitem"
477
+ aria-level={level}
478
+ aria-selected={folder.path === selectedScope}
479
+ aria-expanded={open}
480
+ aria-label={`${folder.path || "Vault root"}, ${folder.conversationCount} conversation${folder.conversationCount === 1 ? "" : "s"}`}
481
+ data-active-folder={folder.path === selectedScope}
482
+ onclick={() => selectFolder(folder.path)}
483
+ >
484
+ <ChevronRightIcon
485
+ class="ai-history__disclosure"
486
+ data-open={open}
487
+ aria-hidden="true"
488
+ />
489
+ {#if open}
490
+ <FolderOpenIcon class="ai-history__folder-icon" aria-hidden="true" />
491
+ {:else}
492
+ <FolderClosedIcon
493
+ class="ai-history__folder-icon"
494
+ aria-hidden="true"
495
+ />
496
+ {/if}
497
+ <span
498
+ class="ai-history__folder-label"
499
+ use:useTextHighlight={{ query, value: folder.name }}
500
+ >{folder.name}</span
501
+ >
502
+ <Sidebar.MenuBadge class="ai-history__count">
503
+ {folder.conversationCount}
504
+ </Sidebar.MenuBadge>
505
+ </Collapsible.Trigger>
506
+ <Collapsible.Content>
507
+ <Sidebar.MenuSub role="group" class="ai-history__subtree">
508
+ {#each folder.children as child (child.path)}
509
+ {@render FolderNode({
510
+ folder: child,
511
+ level: level + 1,
512
+ nested: true,
513
+ })}
514
+ {/each}
515
+ {#each folder.conversations as entry (entryKey(entry))}
516
+ {@render ConversationRow({ entry, level: level + 1 })}
517
+ {/each}
518
+ </Sidebar.MenuSub>
519
+ </Collapsible.Content>
520
+ </Collapsible.Root>
521
+ </Item>
522
+ {/snippet}
523
+
524
+ {#snippet ConversationRow({
525
+ entry,
526
+ level,
527
+ }: {
528
+ entry: ConversationListEntry;
529
+ level: number;
530
+ })}
531
+ {@const title = entry.metadata?.title ?? "Untitled conversation"}
532
+ {@const current = sameLocation(activeConversation, entry.location)}
533
+ <Sidebar.MenuSubItem role="none" class="ai-history__conversation-item">
534
+ <div
535
+ class="ai-history__conversation-row"
536
+ role="treeitem"
537
+ aria-level={level}
538
+ aria-selected={current}
539
+ data-active={current}
540
+ >
541
+ <button
542
+ class="ai-history__open"
543
+ type="button"
544
+ disabled={Boolean(entry.unavailableReason)}
545
+ aria-label={title}
546
+ onclick={() => void onOpenConversation(entry.location)}
547
+ >
548
+ <MessageSquareIcon class="ai-history__chat-icon" aria-hidden="true" />
549
+ <span class="ai-history__conversation-copy">
550
+ <span
551
+ class="ai-history__conversation-title"
552
+ use:useTextHighlight={{ query, value: title }}>{title}</span
553
+ >
554
+ {#if entry.preview}
555
+ <span
556
+ class="ai-history__preview"
557
+ use:useTextHighlight={{ query, value: entry.preview }}
558
+ >{entry.preview}</span
559
+ >
560
+ {:else}
561
+ <span class="ai-history__timestamp">
562
+ {entry.unavailableReason ??
563
+ (entry.metadata
564
+ ? formatChatTimestamp(entry.metadata.updatedAt)
565
+ : "Unavailable")}
566
+ </span>
567
+ {/if}
568
+ </span>
569
+ {#if entry.metadata?.status === "archived"}
570
+ <Badge variant="outline" class="ai-history__archived">Archived</Badge>
571
+ {/if}
572
+ </button>
573
+ {#if entry.metadata}
574
+ <DropdownMenu.Root>
575
+ <DropdownMenu.Trigger>
576
+ {#snippet child({ props }: { props: Record<string, unknown> })}
577
+ <Button
578
+ {...props}
579
+ size="icon-xs"
580
+ variant="ghost"
581
+ class="ai-history__conversation-actions"
582
+ aria-label={`Conversation actions for ${title}`}
583
+ >
584
+ <MoreHorizontalIcon
585
+ data-icon="inline-start"
586
+ aria-hidden="true"
587
+ />
588
+ </Button>
589
+ {/snippet}
590
+ </DropdownMenu.Trigger>
591
+ <DropdownMenu.Content align="end">
592
+ <DropdownMenu.Group>
593
+ <DropdownMenu.Item
594
+ onclick={() =>
595
+ void setArchived(
596
+ entry,
597
+ entry.metadata?.status !== "archived",
598
+ )}
599
+ >
600
+ {#if entry.metadata.status === "archived"}
601
+ <ArchiveRestoreIcon
602
+ data-icon="inline-start"
603
+ aria-hidden="true"
604
+ />
605
+ Restore
606
+ {:else}
607
+ <ArchiveIcon data-icon="inline-start" aria-hidden="true" />
608
+ Archive
609
+ {/if}
610
+ </DropdownMenu.Item>
611
+ <DropdownMenu.Item onclick={() => void remove(entry)}>
612
+ <TrashIcon data-icon="inline-start" aria-hidden="true" />
613
+ Delete
614
+ </DropdownMenu.Item>
615
+ </DropdownMenu.Group>
616
+ </DropdownMenu.Content>
617
+ </DropdownMenu.Root>
618
+ {/if}
619
+ </div>
620
+ </Sidebar.MenuSubItem>
621
+ {/snippet}
622
+
623
+ <style>
624
+ .ai-history {
625
+ --ai-history-surface: var(
626
+ --ui-workspace-view-background,
627
+ var(--ui-workspace-background, var(--background))
628
+ );
629
+ --ai-history-foreground: var(
630
+ --ui-workspace-view-foreground,
631
+ var(--ui-workspace-foreground, var(--foreground))
632
+ );
633
+
634
+ display: flex;
635
+ box-sizing: border-box;
636
+ width: 100%;
637
+ height: 100%;
638
+ min-height: 0;
639
+ flex-direction: column;
640
+ overflow: hidden;
641
+ color: var(--ai-history-foreground);
642
+ background: var(--ai-history-surface);
643
+ font-family: var(--ui-workspace-explorer-font-family, inherit);
644
+ font-size: var(--ui-workspace-explorer-font-size, 0.8125rem);
645
+ --ai-history-count-end-pad: 0.5rem;
646
+ --ai-history-count-width: 2rem;
647
+ }
648
+
649
+ .ai-history__chrome {
650
+ position: relative;
651
+ z-index: 10;
652
+ flex: 0 0 auto;
653
+ background: var(--ai-history-surface);
654
+ --cv-search-filter-background: var(--ai-history-surface);
655
+ --cv-search-filter-justify: center;
656
+ --cv-search-filter-content-justify: center;
657
+ }
658
+
659
+ .ai-history__chrome :global(.cv-search-filter-bar) {
660
+ padding: 0.5rem;
661
+ }
662
+
663
+ .ai-history__chrome :global(.cv-search-filter-bar__filters) {
664
+ flex-basis: 100%;
665
+ }
666
+
667
+ .ai-history__chrome
668
+ :global(.cv-search-filter-bar__actions [data-ui-component="button"]:hover),
669
+ .ai-history__chrome
670
+ :global(
671
+ .cv-search-filter-bar__actions [data-ui-component="button"]:focus-visible
672
+ ),
673
+ .ai-history__chrome :global(.cv-search-filter-bar__filter-toggle:hover),
674
+ .ai-history__chrome
675
+ :global(.cv-search-filter-bar__filter-toggle:focus-visible) {
676
+ background: color-mix(
677
+ in srgb,
678
+ var(--ai-history-foreground) 14%,
679
+ var(--ai-history-surface)
680
+ );
681
+ color: var(--ai-history-foreground);
682
+ }
683
+
684
+ .ai-history__creation-scope {
685
+ margin: 0;
686
+ padding: 0 0.5rem 0.4rem;
687
+ color: color-mix(
688
+ in srgb,
689
+ var(--ai-history-foreground) 68%,
690
+ var(--ai-history-surface)
691
+ );
692
+ font-size: 0.75rem;
693
+ line-height: 1.25rem;
694
+ text-align: center;
695
+ pointer-events: none;
696
+ }
697
+
698
+ .ai-history__archive-filter {
699
+ display: inline-flex;
700
+ align-items: center;
701
+ gap: 0.5rem;
702
+ color: var(--ui-workspace-muted-foreground, var(--muted-foreground));
703
+ font-size: 0.75rem;
704
+ cursor: pointer;
705
+ }
706
+
707
+ :global(.ai-history__scroll) {
708
+ min-height: 0;
709
+ height: 100%;
710
+ flex: 1 1 auto;
711
+ --ui-scroll-area-foreground: var(
712
+ --ui-workspace-border-strong,
713
+ var(--sidebar-border)
714
+ );
715
+ }
716
+
717
+ .ai-history__body {
718
+ box-sizing: border-box;
719
+ min-height: 100%;
720
+ padding: var(--ui-workspace-explorer-content-padding, 0.5rem);
721
+ padding-block-end: 2.5rem;
722
+ }
723
+
724
+ :global(.ai-history__tree),
725
+ :global(.ai-history__folder-item),
726
+ :global(.ai-history__folder-item [data-ui-part="collapsible"]),
727
+ :global(.ai-history__conversation-item) {
728
+ box-sizing: border-box;
729
+ width: 100%;
730
+ min-width: 0;
731
+ }
732
+
733
+ :global(.ai-history__tree) {
734
+ display: flex;
735
+ margin: 0;
736
+ padding: 0;
737
+ list-style: none;
738
+ flex-direction: column;
739
+ gap: var(--ui-workspace-explorer-row-gap, 0.125rem);
740
+ }
741
+
742
+ :global(
743
+ [data-ui-component="sidebar"][data-ui-part="sidebar-menu-sub"].ai-history__subtree
744
+ ) {
745
+ box-sizing: border-box;
746
+ width: calc(100% - var(--ui-workspace-explorer-indent, 0.75rem));
747
+ max-width: 100%;
748
+ margin-inline: 0;
749
+ margin-inline-start: var(--ui-workspace-explorer-indent, 0.75rem);
750
+ padding-inline-start: var(--ui-workspace-explorer-guide-gap, 0.25rem);
751
+ padding-inline-end: 0;
752
+ border-inline-start: var(--ui-workspace-explorer-guide-width, 1px) solid
753
+ var(--ui-workspace-explorer-guide-color, var(--border));
754
+ translate: none;
755
+ }
756
+
757
+ :global(.ai-history__folder-row),
758
+ .ai-history__conversation-row {
759
+ position: relative;
760
+ display: flex;
761
+ box-sizing: border-box;
762
+ width: 100%;
763
+ min-width: 0;
764
+ min-height: var(--ui-workspace-explorer-row-height, 1.75rem);
765
+ align-items: center;
766
+ gap: 0.25rem;
767
+ border: 2px solid transparent;
768
+ border-radius: var(--ui-workspace-radius-small, 0.25rem);
769
+ padding: 0.125rem 0.375rem;
770
+ color: inherit;
771
+ background: transparent;
772
+ font: inherit;
773
+ font-size: inherit;
774
+ line-height: 1.25;
775
+ text-align: start;
776
+ }
777
+
778
+ :global(.ai-history__folder-row) {
779
+ padding-inline-end: calc(
780
+ var(--ai-history-count-width) + var(--ai-history-count-end-pad)
781
+ );
782
+ cursor: pointer;
783
+ }
784
+
785
+ :global(.ai-history__folder-row:hover),
786
+ :global(.ai-history__folder-row:focus-visible),
787
+ .ai-history__conversation-row:hover,
788
+ .ai-history__conversation-row:focus-within {
789
+ background: var(
790
+ --ui-workspace-explorer-row-hover-background,
791
+ var(--sidebar-accent)
792
+ );
793
+ outline: none;
794
+ }
795
+
796
+ :global(.ai-history__folder-row[data-active-folder="true"]),
797
+ .ai-history__conversation-row[data-active="true"] {
798
+ background: var(
799
+ --ui-workspace-explorer-row-active-background,
800
+ var(--accent)
801
+ );
802
+ font-weight: var(--ui-workspace-explorer-row-active-weight, 600);
803
+ }
804
+
805
+ :global(.ai-history__disclosure),
806
+ :global(.ai-history__folder-icon),
807
+ :global(.ai-history__chat-icon) {
808
+ width: 1rem;
809
+ height: 1rem;
810
+ flex: 0 0 auto;
811
+ }
812
+
813
+ :global(.ai-history__disclosure) {
814
+ width: 0.875rem;
815
+ height: 0.875rem;
816
+ transition: transform 120ms ease;
817
+ }
818
+
819
+ :global(.ai-history__disclosure[data-open="true"]) {
820
+ transform: rotate(90deg);
821
+ }
822
+
823
+ :global(.ai-history__folder-icon),
824
+ :global(.ai-history__chat-icon) {
825
+ color: var(--ui-workspace-muted-foreground, var(--muted-foreground));
826
+ }
827
+
828
+ .ai-history__folder-label,
829
+ .ai-history__conversation-title,
830
+ .ai-history__preview,
831
+ .ai-history__timestamp {
832
+ overflow: hidden;
833
+ min-width: 0;
834
+ text-overflow: ellipsis;
835
+ white-space: nowrap;
836
+ }
837
+
838
+ .ai-history__folder-label {
839
+ flex: 1 1 auto;
840
+ }
841
+
842
+ :global(
843
+ [data-ui-component="sidebar"][data-ui-part="sidebar-menu-badge"].ai-history__count
844
+ ) {
845
+ position: absolute;
846
+ top: 50%;
847
+ right: var(--ai-history-count-end-pad);
848
+ z-index: 1;
849
+ display: inline-flex;
850
+ width: var(--ai-history-count-width);
851
+ min-width: var(--ai-history-count-width);
852
+ flex: 0 0 auto;
853
+ align-items: center;
854
+ justify-content: flex-end;
855
+ transform: translateY(-50%);
856
+ pointer-events: none;
857
+ color: var(--ui-workspace-muted-foreground, var(--muted-foreground));
858
+ font-variant-numeric: tabular-nums;
859
+ text-align: end;
860
+ }
861
+
862
+ .ai-history__open {
863
+ display: flex;
864
+ min-width: 0;
865
+ flex: 1 1 auto;
866
+ align-items: center;
867
+ gap: 0.375rem;
868
+ border: 0;
869
+ padding: 0;
870
+ color: inherit;
871
+ background: transparent;
872
+ font: inherit;
873
+ text-align: start;
874
+ cursor: pointer;
875
+ }
876
+
877
+ .ai-history__open:focus-visible {
878
+ outline: none;
879
+ }
880
+
881
+ .ai-history__conversation-copy {
882
+ display: flex;
883
+ min-width: 0;
884
+ flex: 1 1 auto;
885
+ flex-direction: column;
886
+ }
887
+
888
+ .ai-history__conversation-title {
889
+ font-size: 0.75rem;
890
+ }
891
+
892
+ .ai-history__preview,
893
+ .ai-history__timestamp {
894
+ color: var(--ui-workspace-muted-foreground, var(--muted-foreground));
895
+ font-size: 0.6875rem;
896
+ font-weight: 400;
897
+ }
898
+
899
+ :global(.ai-history__archived) {
900
+ flex: 0 0 auto;
901
+ font-size: 0.625rem;
902
+ }
903
+
904
+ :global(.ai-history__conversation-actions) {
905
+ flex: 0 0 auto;
906
+ opacity: 0;
907
+ }
908
+
909
+ .ai-history__conversation-row:hover
910
+ :global(.ai-history__conversation-actions),
911
+ .ai-history__conversation-row:focus-within
912
+ :global(.ai-history__conversation-actions) {
913
+ opacity: 1;
914
+ }
915
+
916
+ .ai-history__state {
917
+ margin: 0;
918
+ padding: 0.5rem 0.75rem;
919
+ color: var(--ui-workspace-muted-foreground, var(--muted-foreground));
920
+ font-size: 0.8125rem;
921
+ }
922
+
923
+ :global(.ai-history .suggestion-highlight) {
924
+ border-radius: 0.125rem;
925
+ color: var(--ui-search-highlight-foreground, inherit);
926
+ background: var(
927
+ --ui-search-highlight-background,
928
+ color-mix(in srgb, var(--primary) 22%, transparent)
929
+ );
930
+ }
931
+
932
+ @media (pointer: coarse) {
933
+ :global(.ai-history__conversation-actions) {
934
+ opacity: 1;
935
+ }
936
+ }
937
+ </style>