@loicngr/kobo 1.7.11 → 1.7.12

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 (106) hide show
  1. package/AGENTS.md +17 -0
  2. package/dist/server/routes/notion.js +12 -1
  3. package/dist/server/routes/templates.js +11 -0
  4. package/dist/server/routes/workspaces.js +31 -0
  5. package/dist/server/services/auto-loop-service.js +2 -0
  6. package/dist/server/services/notion-service.js +113 -0
  7. package/dist/server/services/sentry-service.js +66 -0
  8. package/dist/server/services/settings-service.js +22 -0
  9. package/dist/server/services/skill-suite-prompts.js +9 -4
  10. package/dist/server/services/templates-service.js +72 -72
  11. package/package.json +1 -1
  12. package/src/client/dist/spa/assets/ActivityFeed-FwS4TWtE.js +8 -0
  13. package/src/client/dist/spa/assets/{ClosePopup-Bs-9Klyd.js → ClosePopup-CXORQZjI.js} +1 -1
  14. package/src/client/dist/spa/assets/{CreatePage-D9AbgeOT.js → CreatePage-BEwWpFSM.js} +2 -2
  15. package/src/client/dist/spa/assets/DiffViewer-C5KE6OEn.js +7 -0
  16. package/src/client/dist/spa/assets/HealthPage-C5taBe5N.js +1 -0
  17. package/src/client/dist/spa/assets/MainLayout-Bp0oVWa-.css +1 -0
  18. package/src/client/dist/spa/assets/MainLayout-Cx1buAto.js +37 -0
  19. package/src/client/dist/spa/assets/{QBadge-C7r6oPSi.js → QBadge-u0mEz_W1.js} +1 -1
  20. package/src/client/dist/spa/assets/{QBtn-CaJSOyt8.js → QBtn-Bt7WPzYv.js} +1 -1
  21. package/src/client/dist/spa/assets/{QCheckbox-BvHfXBFY.js → QCheckbox-skYuqkHX.js} +1 -1
  22. package/src/client/dist/spa/assets/{QChip-CQZIeDHV.js → QChip-DuOEr0aU.js} +1 -1
  23. package/src/client/dist/spa/assets/QExpansionItem-Cwk7gp3q.js +1 -0
  24. package/src/client/dist/spa/assets/QIcon-0rjEivgj.js +1 -0
  25. package/src/client/dist/spa/assets/QInput-Ciqjq5-e.js +1 -0
  26. package/src/client/dist/spa/assets/{QItemLabel-CHkgkZVj.js → QItemLabel-B0tYxHQg.js} +1 -1
  27. package/src/client/dist/spa/assets/{QItemSection-Bz1ZDJO5.js → QItemSection-DOOD8VCh.js} +1 -1
  28. package/src/client/dist/spa/assets/{QList-BbnN_oNX.js → QList-MfhZa-uv.js} +1 -1
  29. package/src/client/dist/spa/assets/QMenu-Okyi4ppC.js +1 -0
  30. package/src/client/dist/spa/assets/{QPage-Co2h9wd_.js → QPage-CGYPttdA.js} +1 -1
  31. package/src/client/dist/spa/assets/{QRadio-4HnR_A-K.js → QRadio-DoFiKjZ-.js} +1 -1
  32. package/src/client/dist/spa/assets/QSpace-BrtkvWzZ.js +1 -0
  33. package/src/client/dist/spa/assets/{QSpinnerDots-Bfl2RMy4.js → QSpinnerDots-CluOpUgq.js} +1 -1
  34. package/src/client/dist/spa/assets/{QToggle-DNOTC_3a.js → QToggle-aBvIHg6j.js} +1 -1
  35. package/src/client/dist/spa/assets/QTooltip-CgOu9HtZ.js +1 -0
  36. package/src/client/dist/spa/assets/SearchPage-CdOUi_8L.js +1 -0
  37. package/src/client/dist/spa/assets/SettingsPage-CjBdkOrK.css +1 -0
  38. package/src/client/dist/spa/assets/SettingsPage-Jdin4GXA.js +9 -0
  39. package/src/client/dist/spa/assets/TouchPan-BYRVZE1k.js +1 -0
  40. package/src/client/dist/spa/assets/WorkspacePage-z3JJnma_.js +4 -0
  41. package/src/client/dist/spa/assets/{build-path-tree-DGuSpWFG.js → build-path-tree-BamKKF8S.js} +1 -1
  42. package/src/client/dist/spa/assets/{cssMode-BSeHrn8-.js → cssMode-Co6CFIGH.js} +1 -1
  43. package/src/client/dist/spa/assets/{documents-soWtna0O.js → documents-qOarUoMj.js} +1 -1
  44. package/src/client/dist/spa/assets/{editor.api-Dgjer8XO.js → editor.api-YeMDjOUn.js} +1 -1
  45. package/src/client/dist/spa/assets/{editor.main-DbnmDA5w.js → editor.main-CnghWnPN.js} +3 -3
  46. package/src/client/dist/spa/assets/engineFeatures-DG2lRjWW.js +1 -0
  47. package/src/client/dist/spa/assets/expand-template-DUQqF-Zq.js +1 -0
  48. package/src/client/dist/spa/assets/{formatters-h0XBETG5.js → formatters-ofO3MWTW.js} +1 -1
  49. package/src/client/dist/spa/assets/{freemarker2-C1QMrvUv.js → freemarker2-cMpcLQzW.js} +1 -1
  50. package/src/client/dist/spa/assets/{handlebars-DgiZwx-t.js → handlebars-tAszxpzk.js} +1 -1
  51. package/src/client/dist/spa/assets/{html-CiU03AFd.js → html-B8fakN_A.js} +1 -1
  52. package/src/client/dist/spa/assets/{htmlMode-lj6pNn2T.js → htmlMode-C6OieX5l.js} +1 -1
  53. package/src/client/dist/spa/assets/i18n-BoLFsYKz.js +1 -0
  54. package/src/client/dist/spa/assets/{index-eX_lKHSg.css → index-5ydpLSpt.css} +1 -1
  55. package/src/client/dist/spa/assets/index-BskEbmdO.js +2 -0
  56. package/src/client/dist/spa/assets/{javascript-Bf_RmvhA.js → javascript-D0LpfZ_A.js} +1 -1
  57. package/src/client/dist/spa/assets/{jsonMode-DkzIh92L.js → jsonMode-Doglp1SN.js} +1 -1
  58. package/src/client/dist/spa/assets/{kobo-commands-thKpumUN.js → kobo-commands-CHoV7ovn.js} +1 -1
  59. package/src/client/dist/spa/assets/{liquid-Cg7OHXeJ.js → liquid-CiJ8hylW.js} +1 -1
  60. package/src/client/dist/spa/assets/{mdx-l_W5gHfE.js → mdx-CgUva_L1.js} +1 -1
  61. package/src/client/dist/spa/assets/{monaco.contribution-DBBmYj6f.js → monaco.contribution-DG0R-WYt.js} +2 -2
  62. package/src/client/dist/spa/assets/notifications-CR-qjSNX.js +1 -0
  63. package/src/client/dist/spa/assets/{permissionModes-CRue7srp.js → permissionModes-DbC_sc_F.js} +1 -1
  64. package/src/client/dist/spa/assets/project-color-ANjHSR5A.js +1 -0
  65. package/src/client/dist/spa/assets/purify.es-4cZTowxv.js +60 -0
  66. package/src/client/dist/spa/assets/{python-BEqDMyrj.js → python-Cvb8mJfR.js} +1 -1
  67. package/src/client/dist/spa/assets/{razor-CG3ZSgSC.js → razor-DbKqjziV.js} +1 -1
  68. package/src/client/dist/spa/assets/{render-chat-markdown-C9L-kzlg.js → render-chat-markdown-Ckao9ANM.js} +1 -1
  69. package/src/client/dist/spa/assets/runtime-core.esm-bundler-DPcTPMmX.js +1 -0
  70. package/src/client/dist/spa/assets/{tsMode-CWoApSbW.js → tsMode-0DwTd7Q5.js} +1 -1
  71. package/src/client/dist/spa/assets/{typescript-CTk1WfV6.js → typescript-DDoJP3Mo.js} +1 -1
  72. package/src/client/dist/spa/assets/{use-checkbox-y_fOkYZN.js → use-checkbox-DzLCp4E3.js} +1 -1
  73. package/src/client/dist/spa/assets/use-id-BQW6DfJU.js +1 -0
  74. package/src/client/dist/spa/assets/use-quasar-C5gKpYwL.js +1 -0
  75. package/src/client/dist/spa/assets/{vue-i18n-CKCtKE87.js → vue-i18n-BXnT4vor.js} +2 -2
  76. package/src/client/dist/spa/assets/{xml-CSZMdRGt.js → xml-BrD_WJYH.js} +1 -1
  77. package/src/client/dist/spa/assets/{yaml-tWozZ5pP.js → yaml-BeBHYmTS.js} +1 -1
  78. package/src/client/dist/spa/index.html +13 -13
  79. package/src/client/dist/spa/assets/ActivityFeed-BAASG3py.js +0 -8
  80. package/src/client/dist/spa/assets/DiffViewer-xnpaPL3h.js +0 -7
  81. package/src/client/dist/spa/assets/HealthPage-DWNcejdz.js +0 -1
  82. package/src/client/dist/spa/assets/MainLayout-CQRtNn82.css +0 -1
  83. package/src/client/dist/spa/assets/MainLayout-DZCswY_D.js +0 -37
  84. package/src/client/dist/spa/assets/QExpansionItem-DQ66zLAu.js +0 -1
  85. package/src/client/dist/spa/assets/QIcon-qfJNZLIW.js +0 -1
  86. package/src/client/dist/spa/assets/QInput-DCJEwE8V.js +0 -1
  87. package/src/client/dist/spa/assets/QMenu-pgVPfC7Y.js +0 -1
  88. package/src/client/dist/spa/assets/QSpace-DKIph84L.js +0 -1
  89. package/src/client/dist/spa/assets/QTooltip-CTNw68oZ.js +0 -1
  90. package/src/client/dist/spa/assets/SearchPage-B3emm2UD.js +0 -1
  91. package/src/client/dist/spa/assets/SettingsPage-B3Kn6fwZ.css +0 -1
  92. package/src/client/dist/spa/assets/SettingsPage-j5vhq9YJ.js +0 -9
  93. package/src/client/dist/spa/assets/TouchPan-SGRB-xYE.js +0 -1
  94. package/src/client/dist/spa/assets/WorkspacePage-DmoPjAV6.js +0 -4
  95. package/src/client/dist/spa/assets/engineFeatures-BqiWw0xn.js +0 -1
  96. package/src/client/dist/spa/assets/expand-template-_Y9C3CjP.js +0 -1
  97. package/src/client/dist/spa/assets/i18n-KyQESqOg.js +0 -1
  98. package/src/client/dist/spa/assets/index-ABhgwrBB.js +0 -2
  99. package/src/client/dist/spa/assets/notifications-C4jB7A2f.js +0 -1
  100. package/src/client/dist/spa/assets/project-color-5UmDpA6C.js +0 -1
  101. package/src/client/dist/spa/assets/purify.es-BYydQXtR.js +0 -60
  102. package/src/client/dist/spa/assets/runtime-core.esm-bundler-9Z0QAO_7.js +0 -1
  103. package/src/client/dist/spa/assets/use-id-_7wiRcgb.js +0 -1
  104. package/src/client/dist/spa/assets/use-panel-CZnY7pfY.js +0 -1
  105. package/src/client/dist/spa/assets/use-quasar-DQYS47mh.js +0 -1
  106. /package/src/client/dist/spa/assets/{touch-HRdTUO2o.js → touch-2Qa-HSDZ.js} +0 -0
package/AGENTS.md CHANGED
@@ -273,6 +273,22 @@ These rules are the source of truth and are also written to `.ai/.git-convention
273
273
 
274
274
  The human user of this repository prefers French for conversational exchanges. Code, tests, commit messages, and documentation (including this file) remain in English for toolchain compatibility, but chat responses should be in French unless the user switches.
275
275
 
276
+ ## Design System
277
+
278
+ Always read `DESIGN.md` (repo root) before making any visual or UI decisions. Every
279
+ font choice, color, spacing value, and aesthetic decision is defined there. The CSS
280
+ variables in `src/client/src/css/design-tokens.scss` are the runtime source of truth
281
+ for the values documented in `DESIGN.md` — never hardcode hex colors or spacing
282
+ literals in components.
283
+
284
+ Aesthetic direction: **Brutally Minimal × Industrial** (Linear / Anthropic Console
285
+ reference). Dark-native, monochrome, single indigo accent (`--kobo-accent` /
286
+ `#6c63ff`) used rarely. No purple gradients, no decorative illustrations, no
287
+ bubble-pill shapes, no spring physics. Geist + Geist Mono for technical values.
288
+
289
+ When reviewing or writing UI code, flag any deviation from `DESIGN.md`. Do not
290
+ deviate from the documented system without explicit user approval.
291
+
276
292
  ## What NOT to do
277
293
 
278
294
  - Don't drop-and-recreate the database to apply schema changes. The project is in production — every schema change ships as a migration that preserves data (see [Database migrations](#database-migrations)).
@@ -282,3 +298,4 @@ The human user of this repository prefers French for conversational exchanges. C
282
298
  - Don't break the single-source-of-truth of `CLAUDE.md` → `AGENTS.md` symlink. Edit `AGENTS.md`; `CLAUDE.md` follows automatically.
283
299
  - Don't skip `try/catch` swallowing on best-effort cleanup (agent stop, dev-server stop, worktree removal). These must never break the primary operation.
284
300
  - Don't hardcode user-visible text in the frontend. Every string must go through `$t()` / `t()` with keys in all 5 locale files. See [Internationalization (i18n)](#internationalization-i18n).
301
+ - Don't hardcode hex colors, spacing literals, or font names in components. Use the CSS variables from `src/client/src/css/design-tokens.scss` and the patterns in `DESIGN.md`. See [Design System](#design-system).
@@ -1,7 +1,18 @@
1
1
  import { Hono } from 'hono';
2
- import { extractNotionPage } from '../services/notion-service.js';
2
+ import { extractNotionPage, listNotionUsers } from '../services/notion-service.js';
3
3
  /** Hono sub-router for Notion page extraction. */
4
4
  const app = new Hono();
5
+ // GET /api/notion/users — list workspace users (humans only, for the settings dropdown)
6
+ app.get('/users', async (c) => {
7
+ try {
8
+ const users = await listNotionUsers();
9
+ return c.json({ users });
10
+ }
11
+ catch (err) {
12
+ const message = err instanceof Error ? err.message : String(err);
13
+ return c.json({ error: message }, 500);
14
+ }
15
+ });
5
16
  // POST /api/notion/extract — extract a Notion page
6
17
  app.post('/extract', async (c) => {
7
18
  try {
@@ -64,6 +64,17 @@ app.patch('/:slug', async (c) => {
64
64
  return c.json({ error: message }, statusForServiceError(message));
65
65
  }
66
66
  });
67
+ // POST /api/templates/reload-defaults — re-apply default seed without overwriting user templates
68
+ app.post('/reload-defaults', (c) => {
69
+ try {
70
+ const result = templatesService.reloadDefaultTemplates();
71
+ return c.json(result);
72
+ }
73
+ catch (err) {
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ return c.json({ error: message }, 500);
76
+ }
77
+ });
67
78
  // DELETE /api/templates/:slug — delete a template
68
79
  app.delete('/:slug', (c) => {
69
80
  try {
@@ -161,6 +161,18 @@ app.post('/', migrationGuard, async (c) => {
161
161
  const message = err instanceof Error ? err.message : String(err);
162
162
  return c.json({ error: `Failed to extract Notion page: ${message}` }, 422);
163
163
  }
164
+ const assigneeProperty = settingsService.getGlobalSettings().notionAssigneeProperty;
165
+ if (assigneeProperty && body.notionUrl) {
166
+ const notionUrl = body.notionUrl;
167
+ notionService
168
+ .assignNotionPageToSelf(notionUrl, assigneeProperty)
169
+ .then((result) => {
170
+ if (!result.assigned) {
171
+ console.warn(`[notion] Auto-assign skipped for ${notionUrl}: ${result.reason}`);
172
+ }
173
+ })
174
+ .catch((err) => console.error('[notion] Auto-assign threw unexpectedly:', err));
175
+ }
164
176
  }
165
177
  let sentryContent = null;
166
178
  if (body.sentryUrl) {
@@ -171,6 +183,17 @@ app.post('/', migrationGuard, async (c) => {
171
183
  const message = err instanceof Error ? err.message : String(err);
172
184
  return c.json({ error: `Failed to extract Sentry issue: ${message}` }, 422);
173
185
  }
186
+ if (sentryContent && sentryContent.assignee.length === 0) {
187
+ const sentryUrl = body.sentryUrl;
188
+ sentryService
189
+ .assignSentryIssueToSelf(sentryUrl)
190
+ .then((result) => {
191
+ if (!result.assigned) {
192
+ console.warn(`[sentry] Auto-assign skipped for ${sentryUrl}: ${result.reason}`);
193
+ }
194
+ })
195
+ .catch((err) => console.error('[sentry] Auto-assign threw unexpectedly:', err));
196
+ }
174
197
  }
175
198
  // Create workspace record
176
199
  const globalSettings = settingsService.getGlobalSettings();
@@ -617,6 +640,14 @@ app.post('/', migrationGuard, async (c) => {
617
640
  const suitePrompts = getSuitePrompts(globalSettings.skillSuite, {
618
641
  brainstormingInstruction: globalSettings.customBrainstormingInstruction,
619
642
  });
643
+ // gstack brain sync — load project-specific learnings (patterns, pitfalls,
644
+ // preferences) accumulated by /learn across prior sessions. Only when the
645
+ // active skill suite includes gstack, and only if the user has actually
646
+ // installed gstack locally (the binary may be absent — skip silently).
647
+ // Fast + idempotent; safe to run on every workspace boot.
648
+ if (globalSettings.skillSuite === 'gstack' || globalSettings.skillSuite === 'superpowers+gstack') {
649
+ brainstormPrompt += `\n\nIMPORTANT (gstack): Before brainstorming, check whether \`~/.claude/skills/gstack/bin/gstack-brain-sync\` exists. If it does, run it with \`--once\` to load gstack learnings (project-specific patterns, pitfalls, and preferences from prior sessions) into memory. If the binary is missing (gstack not installed in this environment), skip silently and continue — do NOT install anything and do NOT mention the absence. Fast and idempotent when present.`;
650
+ }
620
651
  if (body.autoLoop === true) {
621
652
  // Auto-loop is armed — brainstorm must end with task seeding + mark-ready,
622
653
  // NOT with implementation. The auto-loop will drive implementation after.
@@ -184,6 +184,8 @@ Current pending task (highest priority, non-acceptance-criterion first):
184
184
  - Title: {taskTitle}
185
185
  - Is acceptance criterion: {isAcceptanceCriterion}
186
186
  {overrideBlock}
187
+ Throughout the steps below, keep the workspace description current via \`kobo__set_workspace_agent_description(description)\` so the user sees your state in the sidebar without opening the workspace. Update it at the start of the iteration (e.g. "Iter #{n}: implementing <short task title>") AND whenever your focus shifts (e.g. "Running tests", "Awaiting code review", "Fixing review feedback", "Marking task done"). Plain text, ≤200 chars.
188
+
187
189
  Your job this iteration:
188
190
  1. Read \`kobo__list_tasks\` to see all tasks and the big picture.
189
191
  2. Implement the SINGLE task above and nothing else. Do not pick a different task.
@@ -269,6 +269,119 @@ export async function extractNotionPage(notionUrl) {
269
269
  mcpProcess.kill();
270
270
  }
271
271
  }
272
+ export async function listNotionUsers() {
273
+ const global = getGlobalSettings();
274
+ const mcpProcess = spawnNotionMcp(global.notionMcpKey);
275
+ try {
276
+ await new Promise((resolve, reject) => {
277
+ const timeout = setTimeout(() => resolve(), 1000);
278
+ mcpProcess.on('error', (err) => {
279
+ clearTimeout(timeout);
280
+ reject(new Error(`Failed to start MCP Notion server: ${err.message}`));
281
+ });
282
+ mcpProcess.stdout?.once('data', () => {
283
+ clearTimeout(timeout);
284
+ resolve();
285
+ });
286
+ });
287
+ await initializeMcp(mcpProcess);
288
+ const collected = [];
289
+ let startCursor;
290
+ for (let i = 0; i < 50; i++) {
291
+ const args = { page_size: 100 };
292
+ if (startCursor)
293
+ args.start_cursor = startCursor;
294
+ const raw = await callMcpTool(mcpProcess, 'API-get-users', args);
295
+ const result = unwrapMcpResult(raw);
296
+ if (!result || typeof result !== 'object')
297
+ break;
298
+ const page = result;
299
+ const results = Array.isArray(page.results) ? page.results : [];
300
+ for (const entry of results) {
301
+ if (entry.type !== 'person')
302
+ continue;
303
+ const id = typeof entry.id === 'string' ? entry.id : '';
304
+ const name = typeof entry.name === 'string' ? entry.name.trim() : '';
305
+ const person = entry.person;
306
+ const email = person && typeof person.email === 'string' ? person.email : '';
307
+ if (!id || !email)
308
+ continue;
309
+ const avatarUrl = typeof entry.avatar_url === 'string' && entry.avatar_url.length > 0 ? entry.avatar_url : null;
310
+ collected.push({ id, name: name || email, email, avatarUrl });
311
+ }
312
+ if (!page.has_more)
313
+ break;
314
+ startCursor = typeof page.next_cursor === 'string' ? page.next_cursor : undefined;
315
+ if (!startCursor)
316
+ break;
317
+ }
318
+ collected.sort((a, b) => a.name.localeCompare(b.name, 'fr'));
319
+ return collected;
320
+ }
321
+ finally {
322
+ mcpProcess.stdin?.end();
323
+ mcpProcess.kill();
324
+ }
325
+ }
326
+ export async function assignNotionPageToSelf(notionUrl, propertyName) {
327
+ if (!propertyName)
328
+ return { assigned: false, reason: 'no property name configured' };
329
+ const global = getGlobalSettings();
330
+ const userId = global.notionUserId.trim();
331
+ if (!userId)
332
+ return { assigned: false, reason: 'no Notion user UUID configured in settings' };
333
+ let pageId;
334
+ try {
335
+ pageId = parseNotionUrl(notionUrl);
336
+ }
337
+ catch (err) {
338
+ return { assigned: false, reason: err instanceof Error ? err.message : String(err) };
339
+ }
340
+ let mcpProcess;
341
+ try {
342
+ mcpProcess = spawnNotionMcp(global.notionMcpKey);
343
+ }
344
+ catch (err) {
345
+ return { assigned: false, reason: err instanceof Error ? err.message : String(err) };
346
+ }
347
+ try {
348
+ await new Promise((resolve, reject) => {
349
+ const timeout = setTimeout(() => resolve(), 1000);
350
+ mcpProcess.on('error', (err) => {
351
+ clearTimeout(timeout);
352
+ reject(new Error(`Failed to start MCP Notion server: ${err.message}`));
353
+ });
354
+ mcpProcess.stdout?.once('data', () => {
355
+ clearTimeout(timeout);
356
+ resolve();
357
+ });
358
+ });
359
+ await initializeMcp(mcpProcess);
360
+ const pageRaw = await callMcpTool(mcpProcess, 'API-retrieve-a-page', { page_id: pageId });
361
+ const pageResult = unwrapMcpResult(pageRaw);
362
+ if (pageResult && typeof pageResult === 'object') {
363
+ const props = pageResult.properties;
364
+ const prop = props?.[propertyName];
365
+ if (prop && Array.isArray(prop.people) && prop.people.length > 0) {
366
+ return { assigned: false, reason: 'property already has an assignee' };
367
+ }
368
+ }
369
+ await callMcpTool(mcpProcess, 'API-patch-page', {
370
+ page_id: pageId,
371
+ properties: {
372
+ [propertyName]: { people: [{ id: userId }] },
373
+ },
374
+ });
375
+ return { assigned: true, reason: `assigned ${userId} to "${propertyName}"` };
376
+ }
377
+ catch (err) {
378
+ return { assigned: false, reason: err instanceof Error ? err.message : String(err) };
379
+ }
380
+ finally {
381
+ mcpProcess.stdin?.end();
382
+ mcpProcess.kill();
383
+ }
384
+ }
272
385
  /** Update a status property on a Notion page. Best-effort, does not throw. */
273
386
  export async function updateNotionStatus(notionUrl, propertyName, statusValue) {
274
387
  const pageId = parseNotionUrl(notionUrl);
@@ -87,6 +87,8 @@ export function parseSentryResponse(markdown, numericId) {
87
87
  const extraData = matchSection(markdown, 'Extra Data');
88
88
  const additionalContext = matchSection(markdown, 'Additional Context');
89
89
  const extraContext = [extraData, additionalContext].filter((s) => s.length > 0).join('\n\n');
90
+ const rawAssignee = matchField(markdown, 'Assigned To') || matchField(markdown, 'Assignee') || matchField(markdown, 'Assigned');
91
+ const assignee = /^(unassigned|none|-|n\/a)$/i.test(rawAssignee.trim()) ? '' : rawAssignee.trim();
90
92
  return {
91
93
  issueId,
92
94
  issueNumericId: numericId,
@@ -100,6 +102,7 @@ export function parseSentryResponse(markdown, numericId) {
100
102
  tags,
101
103
  offendingSpans: parseOffendingSpans(markdown),
102
104
  extraContext,
105
+ assignee,
103
106
  };
104
107
  }
105
108
  // ─── extractSentryIssue ───────────────────────────────────────────────────────
@@ -133,3 +136,66 @@ export async function extractSentryIssue(url) {
133
136
  mcpProcess.kill();
134
137
  }
135
138
  }
139
+ function extractSentryUserId(whoamiResult) {
140
+ if (typeof whoamiResult === 'string') {
141
+ const match = whoamiResult.match(/User\s*ID(?:\s*is)?\s*[:=]?\s*(\d+)/i);
142
+ return match ? match[1] : null;
143
+ }
144
+ if (!whoamiResult || typeof whoamiResult !== 'object')
145
+ return null;
146
+ const r = whoamiResult;
147
+ if (typeof r.id === 'string' && r.id.length > 0)
148
+ return r.id;
149
+ if (typeof r.id === 'number')
150
+ return String(r.id);
151
+ for (const key of ['user', 'data', 'me']) {
152
+ const nested = r[key];
153
+ if (nested && typeof nested === 'object') {
154
+ const inner = nested;
155
+ if (typeof inner.id === 'string' && inner.id.length > 0)
156
+ return inner.id;
157
+ if (typeof inner.id === 'number')
158
+ return String(inner.id);
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ export async function assignSentryIssueToSelf(issueUrl) {
164
+ let config;
165
+ try {
166
+ const global = getGlobalSettings();
167
+ config = readSentryMcpConfig(global.sentryMcpKey);
168
+ }
169
+ catch (err) {
170
+ return { assigned: false, reason: err instanceof Error ? err.message : String(err) };
171
+ }
172
+ const mcpProcess = spawnMcpProcess(config.command, config.args, config.env);
173
+ try {
174
+ await new Promise((resolve, reject) => {
175
+ const timeout = setTimeout(() => resolve(), 1000);
176
+ mcpProcess.on('error', (err) => {
177
+ clearTimeout(timeout);
178
+ reject(new Error(`Failed to start Sentry MCP server: ${err.message}`));
179
+ });
180
+ });
181
+ await initializeMcp(mcpProcess);
182
+ const whoamiRaw = await callMcpTool(mcpProcess, 'whoami', {});
183
+ const whoami = unwrapMcpResult(whoamiRaw);
184
+ const userId = extractSentryUserId(whoami);
185
+ if (!userId) {
186
+ return { assigned: false, reason: 'whoami did not return a user id' };
187
+ }
188
+ await callMcpTool(mcpProcess, 'update_issue', {
189
+ issueUrl,
190
+ assignedTo: `user:${userId}`,
191
+ });
192
+ return { assigned: true, reason: `assigned to user:${userId}` };
193
+ }
194
+ catch (err) {
195
+ return { assigned: false, reason: err instanceof Error ? err.message : String(err) };
196
+ }
197
+ finally {
198
+ mcpProcess.stdin?.end();
199
+ mcpProcess.kill();
200
+ }
201
+ }
@@ -409,6 +409,24 @@ const settingsMigrations = [
409
409
  }
410
410
  },
411
411
  },
412
+ {
413
+ version: 24,
414
+ name: 'add-notion-assignee-property',
415
+ migrate: ({ global }) => {
416
+ if (typeof global.notionAssigneeProperty !== 'string') {
417
+ global.notionAssigneeProperty = '';
418
+ }
419
+ },
420
+ },
421
+ {
422
+ version: 25,
423
+ name: 'add-notion-user-id',
424
+ migrate: ({ global }) => {
425
+ if (typeof global.notionUserId !== 'string') {
426
+ global.notionUserId = '';
427
+ }
428
+ },
429
+ },
412
430
  ];
413
431
  /** Current settings schema version — always equals the highest migration version. */
414
432
  export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
@@ -450,6 +468,8 @@ function defaultSettings() {
450
468
  audioNotificationVolume: 1,
451
469
  notionStatusProperty: '',
452
470
  notionInProgressStatus: '',
471
+ notionAssigneeProperty: '',
472
+ notionUserId: '',
453
473
  defaultPermissionModeByEngine: { 'claude-code': 'plan', codex: 'plan' },
454
474
  notionMcpKey: '',
455
475
  sentryMcpKey: '',
@@ -721,6 +741,8 @@ export function updateGlobalSettings(data) {
721
741
  'audioNotificationVolume',
722
742
  'notionStatusProperty',
723
743
  'notionInProgressStatus',
744
+ 'notionAssigneeProperty',
745
+ 'notionUserId',
724
746
  'defaultPermissionModeByEngine',
725
747
  'notionMcpKey',
726
748
  'sentryMcpKey',
@@ -122,10 +122,15 @@ Superpowers alternative (low-level browser control):
122
122
  For reproducible regression coverage that runs on every PR, prefer **Cypress** specs in \`test/cypress/\` instead of any interactive QA skill. Reserve the tools above for exploration, dogfooding, and one-shot visual debugging.`,
123
123
  brainstormingInstruction: 'Brainstorm using both suites — each plays to its strength:\n' +
124
124
  '1. Early product framing: prefer gstack `/office-hours` for product-shaped work (six forcing questions + design doc). Fall back to `superpowers:brainstorming` for purely infra/refactor work where the product lens does not apply.\n' +
125
- '2. Plan construction: prefer gstack `/autoplan` for the chained CEO/design/eng/DX pipeline. Use `superpowers:writing-plans` instead when you need a TDD-shaped multi-step plan that maps cleanly onto subagent dispatch.\n' +
126
- '3. If you need fine control on either side, invoke the individual skills explicitly (`/plan-ceo-review`, `/plan-eng-review`, ) or stay with the superpowers brainstorm flow.\n' +
127
- '4. For debugging during exploration, prefer `/investigate` (gstack root-cause methodology) or `superpowers:systematic-debugging`, whichever you reach for first.\n' +
128
- '5. Wait for explicit user approval on the final plan before announcing brainstorming is done.',
125
+ '2. Plan construction pick whichever fits the work better:\n' +
126
+ ' - gstack `/autoplan` for the chained CEO/design/eng/DX pipeline (auto-detects which apply).\n' +
127
+ ' - `superpowers:writing-plans` for a TDD-shaped multi-step plan that maps cleanly onto subagent dispatch.\n' +
128
+ '3. For debugging during exploration, prefer `/investigate` (gstack root-cause methodology) or `superpowers:systematic-debugging`, whichever you reach for first.\n' +
129
+ '4. Plan review gate (MANDATORY before announcing brainstorming is done): once the plan is ready, it MUST pass the gstack plan-review skills before you proceed.\n' +
130
+ ' - If you built the plan via `/autoplan`, you have ALREADY passed the chained reviews — skip this step.\n' +
131
+ ' - Otherwise (plan came from `superpowers:writing-plans` or any other path), run `/autoplan` now on the existing plan to chain the reviews, OR invoke the relevant ones individually: `/plan-ceo-review` (scope challenge), `/plan-eng-review` (architecture / edge cases / tests), `/plan-design-review` (UI / AI slop, when there is UI scope), `/plan-devex-review` (when the work has developer-facing surface).\n' +
132
+ ' - Apply any changes the reviews recommend before moving on.\n' +
133
+ '5. Wait for explicit user approval on the final reviewed plan before announcing brainstorming is done.',
129
134
  };
130
135
  /**
131
136
  * Resolve the suite prompts to use right now, given the global `skillSuite`
@@ -126,79 +126,79 @@ export function replaceAllTemplates(templates) {
126
126
  }
127
127
  writeTemplates(validated);
128
128
  }
129
+ export const DEFAULT_TEMPLATES = [
130
+ {
131
+ slug: 'review-quality',
132
+ description: 'Code quality review',
133
+ content: 'Review the recently modified code in {working_branch} for:\n- Logic bugs\n- Missing error handling\n- Style issues\n\nReport only high-confidence findings.',
134
+ },
135
+ {
136
+ slug: 'add-tests',
137
+ description: 'Add unit tests following existing patterns',
138
+ content: 'Add unit tests for the recently modified code. Follow the existing test patterns in this project. Focus on:\n- Happy paths\n- Edge cases\n- Error handling',
139
+ },
140
+ {
141
+ slug: 'explain',
142
+ description: 'Explain the recent changes',
143
+ content: 'Explain what the recently modified code does in {working_branch}, focusing on the non-obvious parts.',
144
+ },
145
+ {
146
+ slug: 'refactor',
147
+ description: 'Safe refactoring',
148
+ content: 'Refactor the selected code to improve readability without changing its behavior. Explain your reasoning as you go.',
149
+ },
150
+ {
151
+ slug: 'plan-tasks',
152
+ description: 'Break work into kobo tasks',
153
+ content: 'Break down the work for this workspace ({workspace_name}) into concrete tasks. Use the kobo-tasks MCP tool `create_task` to register each one with a short, actionable title. Start with a high-level analysis of what needs to happen.',
154
+ },
155
+ {
156
+ slug: 'show-tasks',
157
+ description: 'List current kobo tasks',
158
+ content: 'List the current tasks for this workspace using the kobo-tasks MCP tool `list_tasks`. Show their status and highlight what is still pending.',
159
+ },
160
+ {
161
+ slug: 'mark-done',
162
+ description: 'Mark completed kobo tasks',
163
+ content: 'Review the work completed so far. Identify which tasks from the kobo-tasks list are now done, and mark them using the `mark_task_done` MCP tool.',
164
+ },
165
+ {
166
+ slug: 'sync-tasks',
167
+ description: 'Sync kobo tasks with the codebase',
168
+ content: 'Compare the current state of the codebase against the kobo-tasks list. Create missing tasks with `create_task`, mark completed ones with `mark_task_done`, and delete stale ones with `delete_task`. Explain each change before making it.',
169
+ },
170
+ {
171
+ slug: 'pr-review-comments',
172
+ description: 'List PR review comments requesting changes',
173
+ content: 'Check if a pull request exists for branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to fetch the PR reviews and comments\n2. Filter for reviews with status "CHANGES_REQUESTED"\n3. List each review comment with:\n - The reviewer name\n - The file and line referenced\n - The comment body\n - Whether it has been resolved\n4. Summarize the outstanding requested changes that still need to be addressed\n\nIf no PR exists, say so and suggest pushing the branch first.',
174
+ },
175
+ {
176
+ slug: 'ci-status',
177
+ description: 'Check GitHub Actions status on PR',
178
+ content: 'Check the CI/CD status for the pull request on branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to list the check runs / status checks on the latest commit of the PR\n2. For each check, report:\n - Check name\n - Status (queued, in_progress, completed)\n - Conclusion (success, failure, neutral, skipped, etc.)\n - Duration if available\n3. If any checks failed, fetch the logs or annotations and summarize what went wrong\n4. Give an overall summary: all green, some failing, or still running\n\nIf no PR exists, say so and suggest creating one first.',
179
+ },
180
+ ];
129
181
  function seedTemplates() {
130
182
  const now = new Date().toISOString();
131
- const seed = [
132
- {
133
- slug: 'review-quality',
134
- description: 'Code quality review',
135
- content: 'Review the recently modified code in {working_branch} for:\n- Logic bugs\n- Missing error handling\n- Style issues\n\nReport only high-confidence findings.',
136
- createdAt: now,
137
- updatedAt: now,
138
- },
139
- {
140
- slug: 'add-tests',
141
- description: 'Add unit tests following existing patterns',
142
- content: 'Add unit tests for the recently modified code. Follow the existing test patterns in this project. Focus on:\n- Happy paths\n- Edge cases\n- Error handling',
143
- createdAt: now,
144
- updatedAt: now,
145
- },
146
- {
147
- slug: 'explain',
148
- description: 'Explain the recent changes',
149
- content: 'Explain what the recently modified code does in {working_branch}, focusing on the non-obvious parts.',
150
- createdAt: now,
151
- updatedAt: now,
152
- },
153
- {
154
- slug: 'refactor',
155
- description: 'Safe refactoring',
156
- content: 'Refactor the selected code to improve readability without changing its behavior. Explain your reasoning as you go.',
157
- createdAt: now,
158
- updatedAt: now,
159
- },
160
- {
161
- slug: 'plan-tasks',
162
- description: 'Break work into kobo tasks',
163
- content: 'Break down the work for this workspace ({workspace_name}) into concrete tasks. Use the kobo-tasks MCP tool `create_task` to register each one with a short, actionable title. Start with a high-level analysis of what needs to happen.',
164
- createdAt: now,
165
- updatedAt: now,
166
- },
167
- {
168
- slug: 'show-tasks',
169
- description: 'List current kobo tasks',
170
- content: 'List the current tasks for this workspace using the kobo-tasks MCP tool `list_tasks`. Show their status and highlight what is still pending.',
171
- createdAt: now,
172
- updatedAt: now,
173
- },
174
- {
175
- slug: 'mark-done',
176
- description: 'Mark completed kobo tasks',
177
- content: 'Review the work completed so far. Identify which tasks from the kobo-tasks list are now done, and mark them using the `mark_task_done` MCP tool.',
178
- createdAt: now,
179
- updatedAt: now,
180
- },
181
- {
182
- slug: 'sync-tasks',
183
- description: 'Sync kobo tasks with the codebase',
184
- content: 'Compare the current state of the codebase against the kobo-tasks list. Create missing tasks with `create_task`, mark completed ones with `mark_task_done`, and delete stale ones with `delete_task`. Explain each change before making it.',
185
- createdAt: now,
186
- updatedAt: now,
187
- },
188
- {
189
- slug: 'pr-review-comments',
190
- description: 'List PR review comments requesting changes',
191
- content: 'Check if a pull request exists for branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to fetch the PR reviews and comments\n2. Filter for reviews with status "CHANGES_REQUESTED"\n3. List each review comment with:\n - The reviewer name\n - The file and line referenced\n - The comment body\n - Whether it has been resolved\n4. Summarize the outstanding requested changes that still need to be addressed\n\nIf no PR exists, say so and suggest pushing the branch first.',
192
- createdAt: now,
193
- updatedAt: now,
194
- },
195
- {
196
- slug: 'ci-status',
197
- description: 'Check GitHub Actions status on PR',
198
- content: 'Check the CI/CD status for the pull request on branch {working_branch}.\n\nIf a PR exists (PR {pr_url}):\n1. Use the GitHub MCP tools to list the check runs / status checks on the latest commit of the PR\n2. For each check, report:\n - Check name\n - Status (queued, in_progress, completed)\n - Conclusion (success, failure, neutral, skipped, etc.)\n - Duration if available\n3. If any checks failed, fetch the logs or annotations and summarize what went wrong\n4. Give an overall summary: all green, some failing, or still running\n\nIf no PR exists, say so and suggest creating one first.',
199
- createdAt: now,
200
- updatedAt: now,
201
- },
202
- ];
183
+ const seed = DEFAULT_TEMPLATES.map((t) => ({ ...t, createdAt: now, updatedAt: now }));
203
184
  writeTemplates(seed);
204
185
  }
186
+ export function reloadDefaultTemplates() {
187
+ const existing = listTemplates();
188
+ const existingBySlug = new Map(existing.map((t) => [t.slug, t]));
189
+ const now = new Date().toISOString();
190
+ const added = [];
191
+ const kept = [];
192
+ const next = [...existing];
193
+ for (const def of DEFAULT_TEMPLATES) {
194
+ if (existingBySlug.has(def.slug)) {
195
+ kept.push(def.slug);
196
+ continue;
197
+ }
198
+ next.push({ ...def, createdAt: now, updatedAt: now });
199
+ added.push(def.slug);
200
+ }
201
+ if (added.length > 0)
202
+ writeTemplates(next);
203
+ return { added, kept };
204
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loicngr/kobo",
3
- "version": "1.7.11",
3
+ "version": "1.7.12",
4
4
  "description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -0,0 +1,8 @@
1
+ import{$ as e,E as t,F as n,L as r,M as i,N as a,U as o,W as s,bt as c,d as l,f as u,g as d,h as f,it as p,l as m,p as h,r as g,u as _,v,vt as y,xt as b}from"./runtime-core.esm-bundler-DPcTPMmX.js";import{U as x,l as S,t as C}from"./QIcon-0rjEivgj.js";import{c as w,s as T}from"./notifications-CR-qjSNX.js";import{t as E}from"./QBtn-Bt7WPzYv.js";import{n as D}from"./vue-i18n-BXnT4vor.js";import{c as O,f as ee,h as k,u as A}from"./index-BskEbmdO.js";import{t as j}from"./QSpinnerDots-CluOpUgq.js";import{t as M}from"./QTooltip-CgOu9HtZ.js";import{t as te}from"./QExpansionItem-Cwk7gp3q.js";import{n as ne,r as re,t as N}from"./render-chat-markdown-Ckao9ANM.js";import{n as P}from"./purify.es-4cZTowxv.js";import{t as F}from"./documents-qOarUoMj.js";import{t as I}from"./_plugin-vue_export-helper-Cj6tcsj6.js";function ie(e,t,n=!0){let r=[],i=new Map,a=new Map;for(let n=0;n<e.length;n++){let o=e[n],s=t?.[n];switch(o.kind){case`message:text`:{let e=i.get(o.messageId);if(e)e.text+=o.text,e.streaming=o.streaming;else{let e={type:`text`,messageId:o.messageId,text:o.text,streaming:o.streaming,ts:s};i.set(o.messageId,e),r.push(e)}break}case`message:end`:{let e=i.get(o.messageId);e&&(e.streaming=!1);break}case`message:thinking`:r.push({type:`thinking`,messageId:o.messageId,text:o.text,ts:s});break;case`tool:call`:{let e={type:`tool`,toolCallId:o.toolCallId,name:o.name,input:o.input,ts:s};a.set(o.toolCallId,e),r.push(e);break}case`tool:result`:{let e=a.get(o.toolCallId);e&&(e.result={output:o.output,isError:o.isError});break}case`session:started`:r.push({type:`session`,kind:`started`,detail:{engineSessionId:o.engineSessionId,model:o.model},ts:s});break;case`session:ended`:r.push({type:`session`,kind:`ended`,detail:{reason:o.reason,exitCode:o.exitCode},ts:s});break;case`session:compacted`:r.push({type:`session`,kind:`compacted`,ts:s});break;case`session:brainstorm-complete`:case`session:user-input-requested`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let o=null;for(let e of r)e.type===`text`&&e.streaming&&(o&&(o.streaming=!1),o=e);return o&&!n&&(o.streaming=!1),r}function ae(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}function L(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:`user`;case`session`:return`session`;default:return`agent`}}function oe(e){let t=[],n=null;for(let r of e){let e=L(r),i=e===`session`||e===`system-prompt`;!n||n.speaker!==e||i?(n={speaker:e,ts:r.ts,items:[r]},t.push(n),i&&(n=null)):n.items.push(r)}return t}var R={class:`text-caption text-grey-6`},z=v({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,r=m(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(n(),h(`span`,R,b(e.$t(r.value)),1))}});function se(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){ce(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function ce(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=B(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function B(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var le=[`innerHTML`],V=I(v({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,r=F(),i=O(),a=m(()=>{let e=i.selectedWorkspaceId;return e?r.documentsFor(e).map(e=>e.path):[]}),o=m(()=>ne(se(P.parse(t.item.text,{async:!1,breaks:!0,gfm:!0}),a.value),{addAttr:[`data-document-path`]}));function s(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let n=t.getAttribute(`data-document-path`),a=i.selectedWorkspaceId;!n||!a||r.openDocumentByPath(a,n)}return(t,r)=>(n(),h(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,le),e.item.streaming?(n(),l(S,{key:0,size:`xs`,class:`q-ml-xs`})):u(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]),ue={key:0,class:`text-caption text-grey-5`,style:{"font-style":`italic`}},H=[`innerHTML`],U={key:1,style:{"white-space":`pre-wrap`}},W=I(v({__name:`ThinkingItem`,props:{item:{}},setup(e){let t=e,r=m(()=>t.item.text.trim().slice(0,100)),i=m(()=>t.item.text.trim().length>0),a=m(()=>t.item.text.trim().length>100),o=m(()=>N(t.item.text));return(t,c)=>i.value?(n(),h(`div`,ue,[a.value?(n(),l(te,{key:0,dense:``,"dense-toggle":``,label:r.value,"header-class":`text-grey-5 text-caption`,style:{"font-style":`italic`}},{default:s(()=>[_(`div`,{class:`q-py-xs markdown-thinking`,innerHTML:o.value},null,8,H)]),_:1},8,[`label`])):(n(),h(`span`,U,b(e.item.text),1))])):u(``,!0)}}),[[`__scopeId`,`data-v-7f45ed94`]]);function G(e,t){let n=e.split(`
2
+ `),r=t.split(`
3
+ `),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function de(e){let t=e.split(`
4
+ `),n=[];for(let e of t)e.startsWith(`@@`)||e.startsWith(`+++`)||e.startsWith(`---`)||(e.startsWith(`+`)?n.push({type:`add`,content:e.slice(1)}):e.startsWith(`-`)?n.push({type:`del`,content:e.slice(1)}):e.startsWith(` `)?n.push({type:`context`,content:e.slice(1)}):e.length>0&&n.push({type:`context`,content:e}));return n}function fe(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``,i=typeof n.diff==`string`?n.diff:``;if(!t&&!r&&i.length>0){let t=de(i);return{toolName:`Edit`,filePath:e,additions:t.filter(e=>e.type===`add`).length,deletions:t.filter(e=>e.type===`del`).length,diffLines:t}}return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
5
+ `).length:0,deletions:t?t.split(`
6
+ `).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
7
+ `).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function K(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=q(e,n);if(t!==e)return t}let r=q(e,`${t.projectPath}/${w}/${t.workingBranch}`);return r===e?q(e,t.projectPath):r}function q(e,t){if(!t)return e;let n=pe(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function pe(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(J).join(`[\\\\/]+`)}`:``}function J(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var me={class:`tool-name`},Y=[`title`],X={key:0,class:`tool-stat-add`},he={key:1,class:`tool-stat-del`},ge={class:`diff-sign`},Z={class:`tool-name`},Q=[`title`],$=I(v({__name:`ToolCallItem`,props:{item:{}},setup(t){let i=t,a=e(!1),s=O(),c=m(()=>fe(i.item.name,i.item.input)),p=m(()=>c.value?K(c.value.filePath,s.selectedWorkspace):``),v={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},S=m(()=>v[i.item.name]??`build`),w=m(()=>{if(c.value)return``;let e=i.item.input,t=T(e);return t?K(t,s.selectedWorkspace):``});function T(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let E=m(()=>{let e=c.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?G(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
8
+ `).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),D=m(()=>{let e=i.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),ee=new Set([`Read`]),k=m(()=>!!(i.item.result&&D.value)&&(!ee.has(i.item.name)||i.item.result?.isError===!0));function A(){a.value=!a.value}return o(()=>i.item.result?.isError===!0,e=>{e&&(a.value=!0)},{immediate:!0}),(e,i)=>c.value?(n(),h(`div`,{key:0,class:y([`tool-row`,{"tool-row-expanded":a.value}])},[_(`div`,{class:`tool-header`,onClick:A},[d(C,{name:S.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,me,b(c.value.toolName===`Bash:rm`?`Bash`:c.value.toolName),1),_(`span`,{class:`tool-path`,title:c.value.filePath},b(p.value),9,Y),c.value.additions>0?(n(),h(`span`,X,`+`+b(c.value.additions),1)):u(``,!0),c.value.deletions>0?(n(),h(`span`,he,`-`+b(c.value.deletions),1)):u(``,!0),t.item.result?.isError?(n(),l(C,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):t.item.result?(n(),l(C,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):u(``,!0),d(C,{name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),a.value&&E.value?(n(),h(`div`,{key:0,class:`tool-diff`,onClick:i[0]||=x(()=>{},[`stop`])},[(n(!0),h(g,null,r(E.value,(e,t)=>(n(),h(`div`,{key:t,class:y([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,ge,b(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),f(b(e.content),1)],2))),128))])):u(``,!0)],2)):(n(),h(`div`,{key:1,class:y([`tool-row tool-row-generic`,{"tool-row-expanded":a.value,"tool-row--toggleable":k.value}])},[_(`div`,{class:`tool-header`,onClick:i[1]||=e=>k.value&&A()},[d(C,{name:S.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,Z,b(t.item.name),1),w.value?(n(),h(`span`,{key:0,class:`tool-arg`,title:T(t.item.input)||w.value},b(w.value),9,Q)):u(``,!0),t.item.result?.isError?(n(),l(C,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):t.item.result?(n(),l(C,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):u(``,!0),k.value?(n(),l(C,{key:3,name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):u(``,!0)]),a.value&&k.value?(n(),h(`div`,{key:0,class:`tool-output`,onClick:i[2]||=x(()=>{},[`stop`])},b(D.value),1)):u(``,!0)],2))}}),[[`__scopeId`,`data-v-1702f1be`]]);function _e(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`![${r}](${`/api/workspaces/${encodeURIComponent(t)}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var ve=[`innerHTML`],ye=[`innerHTML`],be=[`src`],xe=I(v({__name:`UserMessageItem`,props:{item:{}},setup(t){let r=t,i=O(),a=m(()=>r.item.sender===`system-prompt`),o=m(()=>N(_e(r.item.content,i.selectedWorkspaceId??``))),c=e(null),f=e(!1);function p(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(c.value=n.src,f.value=!0)}return(e,t)=>(n(),h(g,null,[a.value?(n(),l(te,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:s(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:o.value},null,8,ve)]),_:1},8,[`label`])):(n(),h(`div`,{key:1,class:`markdown-message`,onClick:p},[_(`div`,{innerHTML:o.value},null,8,ye)])),d(k,{modelValue:f.value,"onUpdate:modelValue":t[1]||=e=>f.value=e},{default:s(()=>[c.value?(n(),h(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:t[0]||=e=>f.value=!1},null,8,be)):u(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f34be4c5`]]),Se={class:`turn-header`},Ce={key:0,class:`turn-time`},we={class:`turn-time turn-time-updated`},Te={key:2,class:`turn-actions`},Ee={class:`turn-body`},De={key:0,class:`turn-scroll-top`},Oe=I(v({__name:`TurnCard`,props:{turn:{}},emits:[`scrollTo`],setup(t,{emit:i}){let a=t,o=i,{t:v}=D(),x=e(null);function S(){let e=x.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;o(`scrollTo`,Math.max(0,n-8))}let w=m(()=>{switch(a.turn.speaker){case`user`:return{label:v(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:v(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:v(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:v(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`}}});function T(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let O=m(()=>T(a.turn.ts)),ee=m(()=>{let e=a.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),k=m(()=>{let e=a.turn.ts,t=ee.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:T(t,r-n<6e4)}),A=m(()=>k.value!==``),j=m(()=>a.turn.items.filter(e=>e.type===`tool`).length);return(e,i)=>(n(),h(`div`,{ref_key:`cardEl`,ref:x,class:y([`turn-card`,{"turn-card--user":t.turn.speaker===`user`}]),style:c({"--turn-accent":w.value.accent})},[_(`div`,Se,[_(`span`,{class:y([`turn-badge`,w.value.badgeClass])},b(w.value.label),3),O.value?(n(),h(`span`,Ce,b(O.value),1)):u(``,!0),A.value?(n(),h(g,{key:1},[d(C,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,we,[f(b(k.value)+` `,1),d(M,null,{default:s(()=>[f(b(p(v)(`chat.lastUpdatedAt`,{time:k.value})),1)]),_:1})])],64)):u(``,!0),j.value>0?(n(),h(`span`,Te,` · `+b(p(v)(`chat.nActions`,{n:j.value})),1)):u(``,!0)]),_(`div`,Ee,[(n(!0),h(g,null,r(t.turn.items,(e,t)=>(n(),h(g,{key:t},[e.type===`text`?(n(),l(V,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(n(),l(W,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(n(),l($,{key:2,item:e},null,8,[`item`])):e.type===`user`?(n(),l(xe,{key:3,item:e},null,8,[`item`])):e.type===`session`?(n(),l(z,{key:4,item:e},null,8,[`item`])):u(``,!0)],64))),128))]),t.turn.items.length>4?(n(),h(`div`,De,[d(E,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:S},{default:s(()=>[d(M,null,{default:s(()=>[f(b(p(v)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):u(``,!0)],6))}}),[[`__scopeId`,`data-v-4729e0cc`]]),ke={key:0,class:`activity-feed-switching`},Ae={key:1,class:`activity-feed-wrap`},je={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Me={class:`q-pa-md`},Ne={key:1,class:`q-px-md q-pb-md`},Pe={class:`activity-feed-nav-cluster`},Fe=60,Ie=200,Le=200,Re=400,ze=200,Be=180,Ve=I(v({__name:`ActivityFeed`,props:{workspaceId:{}},setup(c){let p=c,v=ee(),y=T(),x=O(),C=m(()=>x.selectedSessionId),w=m(()=>x.sessions.find(e=>e.id===C.value)?.engineSessionId??null),D=m(()=>{let e=x.sessions;return e.length===0?!1:C.value===e[e.length-1].id});function k(e){return C.value?e?e===C.value||e===w.value:D.value:!0}let M=m(()=>(x.activityFeeds[p.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&k(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId}))),ne=m(()=>A(x.workspaces.find(e=>e.id===p.workspaceId)?.status)),N=m(()=>{let e=v.eventsFor(p.workspaceId),t=v.timestampsFor(p.workspaceId),n=v.sessionIdsFor(p.workspaceId),r=[],i=[];for(let a=0;a<e.length;a++)k(n[a])&&(r.push(e[a]),i.push(t[a]));let a=ae(ie(r,i,ne.value),M.value);return oe(y.showVerboseSystemMessages?a:a.filter(e=>e.type!==`session`))}),P=m(()=>y.showVerboseSystemMessages?v.eventsFor(p.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),F=e(null),I=e(!0),L=e(!1),R=!1,z=e(!0),se=e(new Map);function ce(e){I.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Fe,R&&e.verticalPosition<=Ie&&!L.value&&le()&&H()}function B(e,t){return`${e}:${t}`}function le(){let e=C.value;return e?se.value.get(B(p.workspaceId,e))??!0:v.hasMoreOlderFor(p.workspaceId)}function V(e,t,n){se.value.set(B(e,t),n)}function ue(e){if(!C.value)return v.oldestIdFor(e);let t=v.eventIdsFor(e),n=v.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!k(n[e]))continue;let r=t[e];if(r)return r}}async function H(){let e=p.workspaceId,n=C.value,r=ue(e);if(!r)return;L.value=!0;let i=Date.now();try{let i=F.value;await t();let a=i?.getScroll().verticalSize??0,o=i?.getScroll().verticalPosition??0,s=new URLSearchParams({before:r,limit:`200`});n&&s.set(`session`,n);let c=fetch(`/api/workspaces/${e}/events?${s.toString()}`),l=new Promise(e=>setTimeout(e,Le)),[u]=await Promise.all([c,l]);if(!u.ok){n?V(e,n,!1):v.prepend(e,[],[],{oldestId:r,hasMoreOlder:!1});return}let d=await u.json(),f=d.events??[],p=f.filter(t=>t.type===`agent:event`&&t.workspaceId===e),m=f.filter(t=>t.type===`user:message`&&t.workspaceId===e),h=p.map(e=>e.payload),g=p.map(e=>e.createdAt),_=p.map(e=>e.sessionId??null),y=p.map(e=>e.id),b=f.length>0?f[0].id:r;n&&V(e,n,d.hasMore),v.prepend(e,h,g,{oldestId:b,hasMoreOlder:n?v.hasMoreOlderFor(e):d.hasMore,sessionIds:_,eventIds:y});for(let t of m){let n=t.payload;typeof n.content==`string`&&x.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await t(),i){let e=i.getScroll().verticalSize-a;if(e>0){let t=Math.max(o+e,Ie+50);i.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-i,t=Math.max(0,Le-e);await new Promise(e=>setTimeout(e,t+Re)),L.value=!1}}async function U(e=0){await t();let n=F.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,e)}let W=null,G=0;function de(){if(W!=null)return;let e=performance.now()-G<Be;W=requestAnimationFrame(()=>{W=null,G=performance.now(),U(e?0:180)})}function fe(e){let t=F.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}let K=e([]),q=e(null);function pe(){let e=N.value,t=K.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=q.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function J(){let e=F.value;if(!e)return null;let t=q.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of pe()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function me(){let e=F.value;if(!e)return;let n=J();if(n===null)for(let e=0;e<15&&le();e++){for(;L.value;)await new Promise(e=>setTimeout(e,50));if(await H(),await t(),n=J(),n!==null)break}n!==null&&e.setScrollPosition(`vertical`,Math.max(0,n-12),250)}async function Y(){R=!1,await t(),await U(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{R=!0})})}let X=m(()=>{let e=v.sessionIdsFor(p.workspaceId);if(!C.value)return e.length;let t=0;for(let n of e)k(n)&&t++;return t}),he=m(()=>v.eventsFor(p.workspaceId).length);async function ge(){z.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,ze));let t=e+5e3;for(;he.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));z.value=!1}o(z,async e=>{!e&&X.value>0&&await Y(),!e&&X.value===0&&C.value&&$()}),i(()=>{ge(),X.value>0&&Y(),C.value&&$()});let Z=!1;o(X,async(e,t)=>{if(!Z&&e>0){Z=!0,await Y();return}e>t&&I.value&&!L.value&&de()}),a(()=>{W!=null&&(cancelAnimationFrame(W),W=null)}),o(()=>p.workspaceId,()=>{I.value=!0,Z=!1,R=!1,ge(),X.value>0&&Y()}),o(()=>x.selectedSessionId,async()=>{I.value=!0,R=!1,await Y(),$()});let Q=new Set;async function $(){let e=C.value;if(!e||X.value>0)return;let n=B(p.workspaceId,e);if(!Q.has(n)){Q.add(n);try{let n=await fetch(`/api/workspaces/${p.workspaceId}/events?session=${encodeURIComponent(e)}&limit=500`);if(!n.ok)return;let r=await n.json(),i=r.events??[];if(i.length===0)return;let a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===p.workspaceId),o=i.filter(e=>e.type===`user:message`&&e.workspaceId===p.workspaceId),s=a.map(e=>e.payload),c=a.map(e=>e.createdAt),l=a.map(e=>e.sessionId??null),u=a.map(e=>e.id);V(p.workspaceId,e,r.hasMore),s.length>0&&v.prepend(p.workspaceId,s,c,{oldestId:i[0].id,hasMoreOlder:v.hasMoreOlderFor(p.workspaceId),sessionIds:l,eventIds:u});for(let e of o){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(p.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await t(),await U(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Q.delete(n)}}}o(m(()=>M.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(I.value=!0,await U(180))});async function _e(){I.value=!0,await U(250)}return(e,t)=>z.value?(n(),h(`div`,ke,[d(j,{size:`40px`,color:`indigo-4`})])):(n(),h(`div`,Ae,[d(re,{ref_key:`scrollRef`,ref:F,class:`activity-feed-scroll`,onScroll:ce},{default:s(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:q,class:`content-origin-marker`},null,512),L.value?(n(),h(`div`,je,[d(S,{size:`sm`}),f(` `+b(e.$t(`activity.loading_older`)),1)])):u(``,!0),_(`div`,Me,[(n(!0),h(g,null,r(N.value,(e,t)=>(n(),l(Oe,{key:t,ref_for:!0,ref_key:`turnRefs`,ref:K,turn:e,onScrollTo:fe},null,8,[`turn`]))),128))]),P.value.length?(n(),h(`div`,Ne,[d(te,{label:e.$t(`activity.raw_lines`,{n:P.value.length}),dense:``},{default:s(()=>[(n(!0),h(g,null,r(P.value,(e,t)=>(n(),h(`div`,{key:t,class:`text-caption text-grey q-pa-xs`},b(e),1))),128))]),_:1},8,[`label`])])):u(``,!0)]),_:1},512),_(`div`,Pe,[I.value?u(``,!0):(n(),l(E,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:_e},null,8,[`title`])),d(E,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:me},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-c133a623`]]);export{Ve as default};
@@ -1 +1 @@
1
- import{F as e,b as t}from"./QIcon-qfJNZLIW.js";import{C as n,w as r}from"./notifications-C4jB7A2f.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=r(e);i!==void 0&&n(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};
1
+ import{F as e,b as t}from"./QIcon-0rjEivgj.js";import{T as n,w as r}from"./notifications-CR-qjSNX.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=n(e);i!==void 0&&r(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};