@loicngr/kobo 1.11.16 → 1.11.17

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 (175) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +9 -0
  3. package/dist/mcp-server/kobo-tasks-server.js +8 -7
  4. package/dist/server/db/migrations.js +57 -0
  5. package/dist/server/db/schema.js +10 -39
  6. package/dist/server/index.js +55 -7
  7. package/dist/server/routes/git.js +8 -3
  8. package/dist/server/routes/health.js +19 -2
  9. package/dist/server/routes/settings.js +24 -0
  10. package/dist/server/routes/workspaces.js +371 -72
  11. package/dist/server/services/agent/engines/claude-code/engine.js +39 -35
  12. package/dist/server/services/agent/engines/codex/client.js +16 -4
  13. package/dist/server/services/agent/engines/codex/engine.js +23 -2
  14. package/dist/server/services/agent/engines/codex/jsonrpc/peer.js +44 -5
  15. package/dist/server/services/agent/engines/types.js +13 -0
  16. package/dist/server/services/agent/orchestrator.js +354 -133
  17. package/dist/server/services/agent/session-controller.js +27 -0
  18. package/dist/server/services/agent/{engines/codex/turn-liveness.js → turn-liveness.js} +6 -2
  19. package/dist/server/services/change-source-branch-service.js +115 -11
  20. package/dist/server/services/db-backup-service.js +27 -16
  21. package/dist/server/services/pr-watcher-service.js +17 -2
  22. package/dist/server/services/settings-service.js +39 -0
  23. package/dist/server/services/websocket-service.js +114 -48
  24. package/dist/server/services/workspace-service.js +54 -2
  25. package/dist/server/services/worktree-purge-service.js +19 -5
  26. package/dist/server/services/worktree-service.js +38 -1
  27. package/dist/server/services/ws-events-retention-service.js +133 -0
  28. package/dist/server/utils/git-ops.js +209 -14
  29. package/dist/server/utils/git-repo-lock.js +45 -0
  30. package/dist/server/utils/logger.js +108 -0
  31. package/dist/server/utils/script-runner.js +11 -0
  32. package/package.json +1 -1
  33. package/src/client/dist/pwa/assets/ActivityFeed-BlDBh5jB.js +8 -0
  34. package/src/client/dist/pwa/assets/ActivityFeed-tC8sR0GV.css +1 -0
  35. package/src/client/dist/pwa/assets/ChangelogPage-DACtknly.js +1 -0
  36. package/src/client/dist/pwa/assets/ChangelogPage-D_WOVByV.css +1 -0
  37. package/src/client/dist/pwa/assets/ClosePopup-DEy0yFXE.js +1 -0
  38. package/src/client/dist/pwa/assets/CreatePage-Bb-Qn-Rd.js +2 -0
  39. package/src/client/dist/pwa/assets/CreatePage-RMBX-Zjn.css +1 -0
  40. package/src/client/dist/pwa/assets/DiffViewer-DKt6gFi4.js +8 -0
  41. package/src/client/dist/pwa/assets/DiffViewer-ahWtFrRy.css +1 -0
  42. package/src/client/dist/pwa/assets/DocumentsPanel-B8QY7FTj.css +1 -0
  43. package/src/client/dist/pwa/assets/DocumentsPanel-DhP1jk11.js +1 -0
  44. package/src/client/dist/pwa/assets/DrawerToggleButton-CBdOJzVZ.js +1 -0
  45. package/src/client/dist/pwa/assets/GitPanel-BKrtkPrV.css +1 -0
  46. package/src/client/dist/pwa/assets/GitPanel-DNCCB0G_.js +3 -0
  47. package/src/client/dist/pwa/assets/HealthPage-B1J4-dOH.js +1 -0
  48. package/src/client/dist/pwa/assets/HealthPage-BbqLoFQj.css +1 -0
  49. package/src/client/dist/pwa/assets/MainLayout-F8ShDnAy.css +1 -0
  50. package/src/client/dist/pwa/assets/MainLayout-qWwDy1B5.js +2 -0
  51. package/src/client/dist/pwa/assets/QBadge-BgS0-MXB.js +1 -0
  52. package/src/client/dist/pwa/assets/QBanner-BoAbhM1O.js +1 -0
  53. package/src/client/dist/pwa/assets/QBtn-B7WFdfmy.js +1 -0
  54. package/src/client/dist/pwa/assets/QBtnGroup-BMQlX2YS.js +1 -0
  55. package/src/client/dist/pwa/assets/QBtnToggle-eMu_yinY.js +1 -0
  56. package/src/client/dist/pwa/assets/QChip-BzB_GF4Z.js +1 -0
  57. package/src/client/dist/pwa/assets/QExpansionItem-C8CWo-aP.js +1 -0
  58. package/src/client/dist/pwa/assets/QItemSection-CDBweQnA.js +1 -0
  59. package/src/client/dist/pwa/assets/QList-Ccqhimcb.js +1 -0
  60. package/src/client/dist/pwa/assets/QPage-RpDxx5ah.js +1 -0
  61. package/src/client/dist/pwa/assets/QSelect-D9bHHxAA.js +1 -0
  62. package/src/client/dist/pwa/assets/QSlideTransition-6lWJvjLP.js +1 -0
  63. package/src/client/dist/pwa/assets/QSpace-D3PNLwa4.js +1 -0
  64. package/src/client/dist/pwa/assets/QSpinnerDots-DWOFEezR.js +1 -0
  65. package/src/client/dist/pwa/assets/SchedulePanel-Brtqtz1H.js +1 -0
  66. package/src/client/dist/pwa/assets/SchedulePanel-BunlzsOY.css +1 -0
  67. package/src/client/dist/pwa/assets/SearchPage-CvZxJiJs.js +1 -0
  68. package/src/client/dist/pwa/assets/SearchPage-DdWM01rq.css +1 -0
  69. package/src/client/dist/pwa/assets/SettingsPage-JVje8OPs.js +16 -0
  70. package/src/client/dist/pwa/assets/SettingsPage-dMAg4JP8.css +1 -0
  71. package/src/client/dist/pwa/assets/TerminalPanel-B6xJJs8p.css +1 -0
  72. package/src/client/dist/pwa/assets/TerminalPanel-Dtc6B3US.js +18 -0
  73. package/src/client/dist/pwa/assets/TouchPan-DnEDaTFL.js +1 -0
  74. package/src/client/dist/pwa/assets/WorkspacePage-BVxtefbj.js +4 -0
  75. package/src/client/dist/pwa/assets/WorkspacePage-DN-A6XpI.css +1 -0
  76. package/src/client/dist/pwa/assets/build-path-tree-BU8Y8vRw.js +1 -0
  77. package/src/client/dist/pwa/assets/clipboard-t9qe6lK7.js +1 -0
  78. package/src/client/dist/pwa/assets/{cssMode-DNEi2cLH.js → cssMode-D_JDSbNP.js} +1 -1
  79. package/src/client/dist/pwa/assets/debounce-CDIE7CV6.js +1 -0
  80. package/src/client/dist/pwa/assets/documents-B6eOZeFK.js +1 -0
  81. package/src/client/dist/pwa/assets/{editor.api-_tFrtCR7.js → editor.api-Ccbr7NXr.js} +1 -1
  82. package/src/client/dist/pwa/assets/engineFeatures-DW9PydRL.js +1 -0
  83. package/src/client/dist/pwa/assets/expand-template-B4LAsXAF.css +1 -0
  84. package/src/client/dist/pwa/assets/expand-template-Xq6JwXsl.js +1 -0
  85. package/src/client/dist/pwa/assets/{freemarker2-kfVsFQjE.js → freemarker2-CUvQzJdx.js} +1 -1
  86. package/src/client/dist/pwa/assets/{handlebars-EwBi9SHt.js → handlebars-BXiAPlES.js} +1 -1
  87. package/src/client/dist/pwa/assets/{html-BOOCP_vn.js → html-CLmfyTMC.js} +1 -1
  88. package/src/client/dist/pwa/assets/{htmlMode-IN9oEIXW.js → htmlMode-DWYo6_e_.js} +1 -1
  89. package/src/client/dist/pwa/assets/i18n-CJclRByY.js +1 -0
  90. package/src/client/dist/pwa/assets/index-Bri8-39E.css +1 -0
  91. package/src/client/dist/pwa/assets/index-Dv1p-1sA.js +82 -0
  92. package/src/client/dist/pwa/assets/{javascript-CfV8qmeH.js → javascript-C9gWFLC_.js} +1 -1
  93. package/src/client/dist/pwa/assets/{jsonMode-C0b3m4VO.js → jsonMode-CbsjeFb8.js} +1 -1
  94. package/src/client/dist/pwa/assets/kobo-commands-DGygmOvc.js +9 -0
  95. package/src/client/dist/pwa/assets/layout-nWIyHeJf.js +1 -0
  96. package/src/client/dist/pwa/assets/{liquid-D3YK93J-.js → liquid-BqJ3SIha.js} +1 -1
  97. package/src/client/dist/pwa/assets/{lspLanguageFeatures-PklP2gEH.js → lspLanguageFeatures-BB0y1B51.js} +1 -1
  98. package/src/client/dist/pwa/assets/{mdx-DjZcevPS.js → mdx-rIxQLZDB.js} +1 -1
  99. package/src/client/dist/pwa/assets/network-auth-pzKTXRYP.js +1 -0
  100. package/src/client/dist/pwa/assets/notify-theme-D7RrwvJa.js +1 -0
  101. package/src/client/dist/pwa/assets/pending-diff-open-Cia8sndE.js +1 -0
  102. package/src/client/dist/pwa/assets/private.use-form-D8Yuc3aU.js +1 -0
  103. package/src/client/dist/pwa/assets/{project-color-Cc9yKGk3.js → project-color-Dur1z6Gn.js} +1 -1
  104. package/src/client/dist/pwa/assets/{python-BrgzhqrC.js → python-C6d5du9h.js} +1 -1
  105. package/src/client/dist/pwa/assets/{razor-g8NeeeQc.js → razor-Aek1yDc0.js} +1 -1
  106. package/src/client/dist/pwa/assets/{render-chat-markdown-CZyJL1-w.js → render-chat-markdown-B6sa44Xr.js} +2 -2
  107. package/src/client/dist/pwa/assets/skill-suite-prompts-DdoGRwhz.js +36 -0
  108. package/src/client/dist/pwa/assets/{tsMode-DAopv4hH.js → tsMode-rxNEyqyY.js} +1 -1
  109. package/src/client/dist/pwa/assets/{typescript-C2jZCVPF.js → typescript-DndxWYhC.js} +1 -1
  110. package/src/client/dist/pwa/assets/use-checkbox-z-Qs4ZHY.js +1 -0
  111. package/src/client/dist/pwa/assets/use-id-DlouqdB8.js +1 -0
  112. package/src/client/dist/pwa/assets/use-onboarding-BzSwN4xT.js +2 -0
  113. package/src/client/dist/pwa/assets/{vs-mk-rn9sP.js → vs-De8HKJ9r.js} +3 -3
  114. package/src/client/dist/pwa/assets/{workers-CP5iY9Kz.js → workers-DdNkyVm9.js} +1 -1
  115. package/src/client/dist/pwa/assets/{xml-D3pYNERH.js → xml-Cl1k1mQr.js} +1 -1
  116. package/src/client/dist/pwa/assets/{yaml-BTRPY5XP.js → yaml-C8G_k5TR.js} +1 -1
  117. package/src/client/dist/pwa/fonts/GEIST-LICENSE.txt +92 -0
  118. package/src/client/dist/pwa/fonts/Geist-Variable.woff2 +0 -0
  119. package/src/client/dist/pwa/fonts/GeistMono-Variable.woff2 +0 -0
  120. package/src/client/dist/pwa/index.html +7 -5
  121. package/src/client/dist/pwa/sw.js +1 -1
  122. package/src/mcp-server/kobo-tasks-server.ts +8 -7
  123. package/dist/server/utils/process-tracker.js +0 -49
  124. package/src/client/dist/pwa/assets/ActivityFeed-BrjeLlhi.css +0 -1
  125. package/src/client/dist/pwa/assets/ActivityFeed-WBBk7DgE.js +0 -8
  126. package/src/client/dist/pwa/assets/ChangelogPage-Cr5cW9Wx.js +0 -1
  127. package/src/client/dist/pwa/assets/ChangelogPage-DGhfnkax.css +0 -1
  128. package/src/client/dist/pwa/assets/ClosePopup-2KSSXX6P.js +0 -1
  129. package/src/client/dist/pwa/assets/CreatePage-Bl1Vjr27.css +0 -1
  130. package/src/client/dist/pwa/assets/CreatePage-DQk8TrUP.js +0 -2
  131. package/src/client/dist/pwa/assets/DiffViewer-C9xV8dh-.js +0 -8
  132. package/src/client/dist/pwa/assets/DiffViewer-CS3P9Ngi.css +0 -1
  133. package/src/client/dist/pwa/assets/DrawerToggleButton-CjM28eaw.js +0 -1
  134. package/src/client/dist/pwa/assets/HealthPage-BbLpLRjV.js +0 -1
  135. package/src/client/dist/pwa/assets/HealthPage-BqirO5le.css +0 -1
  136. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-BCOjigVR.woff +0 -0
  137. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-Ocp2tNig.woff +0 -0
  138. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-6jEn9xEl.woff +0 -0
  139. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-CCs8I9zJ.woff +0 -0
  140. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-BLaboPi0.woff +0 -0
  141. package/src/client/dist/pwa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-DHuK6FBl.woff +0 -0
  142. package/src/client/dist/pwa/assets/MainLayout-BUril7SK.js +0 -19
  143. package/src/client/dist/pwa/assets/MainLayout-u-AM87m3.css +0 -1
  144. package/src/client/dist/pwa/assets/QBadge-DS9cpnyx.js +0 -1
  145. package/src/client/dist/pwa/assets/QBanner-tANb95XA.js +0 -1
  146. package/src/client/dist/pwa/assets/QBtn-DJGkBel8.js +0 -1
  147. package/src/client/dist/pwa/assets/QBtnToggle-D-Hsqmcb.js +0 -1
  148. package/src/client/dist/pwa/assets/QChip-iLXQ5Khf.js +0 -1
  149. package/src/client/dist/pwa/assets/QItemSection-B-5iFRBZ.js +0 -1
  150. package/src/client/dist/pwa/assets/QList-D-yd1vEv.js +0 -1
  151. package/src/client/dist/pwa/assets/QSelect-TFRaHbzG.js +0 -36
  152. package/src/client/dist/pwa/assets/QSeparator-C_2BuEw9.js +0 -1
  153. package/src/client/dist/pwa/assets/QSpace-BuMMeXnK.js +0 -1
  154. package/src/client/dist/pwa/assets/QSpinnerDots-C-tv95pD.js +0 -1
  155. package/src/client/dist/pwa/assets/SearchPage-CPM7L9_M.js +0 -1
  156. package/src/client/dist/pwa/assets/SearchPage-CngtAqls.css +0 -1
  157. package/src/client/dist/pwa/assets/SettingsPage-BRYycuYY.js +0 -16
  158. package/src/client/dist/pwa/assets/SettingsPage-D5BacmAr.css +0 -1
  159. package/src/client/dist/pwa/assets/TouchPan-79_u9-Fd.js +0 -1
  160. package/src/client/dist/pwa/assets/WorkspacePage-DB1n1bmg.js +0 -4
  161. package/src/client/dist/pwa/assets/WorkspacePage-hxEpXqBA.css +0 -1
  162. package/src/client/dist/pwa/assets/documents-BMvE9Q_U.js +0 -1
  163. package/src/client/dist/pwa/assets/engineFeatures-JkVb6hry.js +0 -1
  164. package/src/client/dist/pwa/assets/expand-template-D2yUa54D.css +0 -1
  165. package/src/client/dist/pwa/assets/expand-template-Dl4wUjF8.js +0 -1
  166. package/src/client/dist/pwa/assets/i18n-Dezgz2hU.js +0 -1
  167. package/src/client/dist/pwa/assets/index-B12jWMl8.js +0 -82
  168. package/src/client/dist/pwa/assets/index-CK1nk7qr.css +0 -1
  169. package/src/client/dist/pwa/assets/kobo-commands-DznaKlq6.js +0 -9
  170. package/src/client/dist/pwa/assets/layout-me0gwmBV.js +0 -1
  171. package/src/client/dist/pwa/assets/network-auth-BMTuTKHx.js +0 -1
  172. package/src/client/dist/pwa/assets/pending-diff-open-BG3IOnWw.js +0 -1
  173. package/src/client/dist/pwa/assets/use-checkbox-D-9LXk-S.js +0 -1
  174. package/src/client/dist/pwa/assets/use-is-mobile-DX2Fiee-.js +0 -1
  175. package/src/client/dist/pwa/assets/use-onboarding-BNo8mb65.js +0 -2
@@ -5,6 +5,43 @@ import { promisify } from 'node:util';
5
5
  import { resolvePathInside } from './safe-path.js';
6
6
  const execFileAsync = promisify(execFileCb);
7
7
  const READ_ONLY_GIT_TIMEOUT_MS = 15_000;
8
+ /** Plafond partagé par les appels git synchrones. Le défaut de Node (1 Mo)
9
+ * transforme les gros diffs et les gros fichiers en `ENOBUFS`, erreur
10
+ * ensuite avalée par les `catch` de ce fichier — un fichier de 2 Mo
11
+ * s'affichait donc vide dans le visualiseur de diff. */
12
+ const GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
13
+ /** Un git synchrone qui pend bloque TOUTE la boucle d'événements Node : plus
14
+ * de HTTP, plus de WebSocket, plus d'événements agent. Les opérations
15
+ * réseau (`fetch`, `push`, `pull`) sont les seules réellement exposées ;
16
+ * 60 s leur laissent de la marge sur un gros dépôt tout en garantissant que
17
+ * le serveur repart. */
18
+ const SYNC_GIT_TIMEOUT_MS = 60_000;
19
+ /** Empêche git d'ouvrir une invite de mot de passe : sans cela, un dépôt
20
+ * privé sans identifiants en cache fait pendre le process indéfiniment.
21
+ * Exported as a function (not a plain module-level object) so tests can
22
+ * mutate `process.env.GIT_SSH_COMMAND` and observe the effect — the
23
+ * constant below is evaluated once at import time and would not reflect
24
+ * a later mutation. */
25
+ export function buildNonInteractiveGitEnv() {
26
+ return {
27
+ ...process.env,
28
+ GIT_TERMINAL_PROMPT: '0',
29
+ GIT_ASKPASS: 'echo',
30
+ // `GIT_ASKPASS` only covers HTTP(S) credential prompts. A passphrase-
31
+ // protected SSH key with no running agent still blocks `ssh` on
32
+ // `/dev/tty`; `BatchMode=yes` makes that interactive auth fail in ~1s
33
+ // instead of hanging until the 60s sync-git timeout above catches it.
34
+ // Preserve a user-provided `GIT_SSH_COMMAND` (custom identity, host alias,
35
+ // non-standard port) and only append the non-interactive flag. ssh resolves
36
+ // each option to its FIRST occurrence, so an explicit `BatchMode` set by the
37
+ // user still wins — their intent is respected, and the 60 s timeout remains
38
+ // the backstop.
39
+ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND
40
+ ? `${process.env.GIT_SSH_COMMAND} -o BatchMode=yes`
41
+ : 'ssh -o BatchMode=yes',
42
+ };
43
+ }
44
+ const NON_INTERACTIVE_GIT_ENV = buildNonInteractiveGitEnv();
8
45
  function git(repoPath, args) {
9
46
  // `trimEnd` (not `trim`): some git outputs are column-aligned and the LEADING
10
47
  // space carries information. The classic case is `git status --porcelain`,
@@ -13,7 +50,13 @@ function git(repoPath, args) {
13
50
  // and makes `line.substring(3)` chop the first character of the filename
14
51
  // (e.g. `front/foo` → `ront/foo`). Trailing whitespace (the final `\n` git
15
52
  // always appends) still goes — that's what every caller expects.
16
- return execFileSync('git', args, { cwd: repoPath, encoding: 'utf-8' }).trimEnd();
53
+ return execFileSync('git', args, {
54
+ cwd: repoPath,
55
+ encoding: 'utf-8',
56
+ timeout: SYNC_GIT_TIMEOUT_MS,
57
+ maxBuffer: GIT_MAX_BUFFER_BYTES,
58
+ env: NON_INTERACTIVE_GIT_ENV,
59
+ }).trimEnd();
17
60
  }
18
61
  async function gitAsync(repoPath, args, timeout = READ_ONLY_GIT_TIMEOUT_MS) {
19
62
  const { stdout } = await execFileAsync('git', args, {
@@ -24,6 +67,42 @@ async function gitAsync(repoPath, args, timeout = READ_ONLY_GIT_TIMEOUT_MS) {
24
67
  });
25
68
  return stdout.trimEnd();
26
69
  }
70
+ /** Raised when a stale `index.lock` blocks every write on a worktree. */
71
+ export class GitIndexLockError extends Error {
72
+ lockPath;
73
+ constructor(lockPath) {
74
+ super(`The git index of this worktree is locked by '${lockPath}'. A previous git command was killed before it ` +
75
+ `could release it — typically a setup or cleanup script that hit its timeout and was SIGKILLed mid-commit. ` +
76
+ `If no git process is running on this worktree, remove the file: rm -f '${lockPath}'`);
77
+ this.name = 'GitIndexLockError';
78
+ this.lockPath = lockPath;
79
+ }
80
+ }
81
+ /**
82
+ * Absolute path of this worktree's `index.lock` when it exists, else `null`.
83
+ *
84
+ * The lock lives in the worktree's OWN git dir (`--absolute-git-dir`), not in
85
+ * the common dir shared with the other worktrees: each worktree has its own
86
+ * index. Returns `null` rather than throwing outside a repository — callers use
87
+ * this to produce a better error, never to decide whether git is usable.
88
+ */
89
+ export function getIndexLockPath(repoPath) {
90
+ let gitDir;
91
+ try {
92
+ gitDir = git(repoPath, ['rev-parse', '--absolute-git-dir']);
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ const lockPath = join(gitDir, 'index.lock');
98
+ return existsSync(lockPath) ? lockPath : null;
99
+ }
100
+ /** Throw `GitIndexLockError` when a stale index lock would make every write fail. */
101
+ export function assertNoIndexLock(repoPath) {
102
+ const lockPath = getIndexLockPath(repoPath);
103
+ if (lockPath)
104
+ throw new GitIndexLockError(lockPath);
105
+ }
27
106
  /** Return the name of the currently checked-out branch. */
28
107
  export function getCurrentBranch(repoPath) {
29
108
  return git(repoPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
@@ -446,20 +525,63 @@ export function reconstructBranchOnto(repoPath, workingBranch, newBase, commits)
446
525
  }
447
526
  return backupBranch;
448
527
  }
449
- /** List `kobo-backup/<workingBranch>-<ts>` branches, newest timestamp first. */
528
+ /** List `kobo-backup/<workingBranch>-<ts>` branches, newest timestamp first.
529
+ *
530
+ * THROWS on a git failure instead of returning an empty list: an empty list is
531
+ * the caller's signal that no rollback exists, and swallowing an inherited
532
+ * lock or a buffer overflow here made the cancel route answer "automatic
533
+ * restore impossible" while the backup branch was sitting right there. */
450
534
  export function listBackupBranches(repoPath, workingBranch) {
535
+ const prefix = `kobo-backup/${workingBranch}-`;
536
+ let out;
451
537
  try {
452
- const prefix = `kobo-backup/${workingBranch}-`;
453
- const out = git(repoPath, ['branch', '--list', `${prefix}*`, '--format=%(refname:short)']);
454
- return out
455
- .split('\n')
456
- .map((s) => s.trim())
457
- .filter((b) => b.startsWith(prefix) && /^\d+$/.test(b.slice(prefix.length)))
458
- .sort((a, b) => Number(b.slice(prefix.length)) - Number(a.slice(prefix.length)));
538
+ out = git(repoPath, ['branch', '--list', `${prefix}*`, '--format=%(refname:short)']);
459
539
  }
460
- catch {
461
- return [];
540
+ catch (err) {
541
+ const message = err instanceof Error ? err.message : String(err);
542
+ throw new Error(`Failed to list backup branches for '${workingBranch}': ${message}`);
543
+ }
544
+ return out
545
+ .split('\n')
546
+ .map((s) => s.trim())
547
+ .filter((b) => b.startsWith(prefix) && /^\d+$/.test(b.slice(prefix.length)))
548
+ .sort((a, b) => Number(b.slice(prefix.length)) - Number(a.slice(prefix.length)));
549
+ }
550
+ /** Keep the `keep` newest backup branches of `workingBranch`, delete the rest.
551
+ *
552
+ * Each backup branch pins every commit of a previous version of the branch, so
553
+ * git's garbage collector can never reclaim them: without rotation the
554
+ * repository grows by one full branch history per source-branch change, for
555
+ * ever. Best-effort — a failure to delete one branch must not fail the
556
+ * operation that just succeeded.
557
+ *
558
+ * Never throws, but never stays silent either: "nothing to rotate" and "the
559
+ * rotation could not run" are two different answers, and reporting the second
560
+ * as the first turns a persistent git failure into unbounded growth that only
561
+ * a server log would ever have revealed. */
562
+ export function pruneBackupBranches(repoPath, workingBranch, keep = 3) {
563
+ const warnings = [];
564
+ let branches;
565
+ try {
566
+ branches = listBackupBranches(repoPath, workingBranch);
567
+ }
568
+ catch (err) {
569
+ const message = err instanceof Error ? err.message : String(err);
570
+ warnings.push(`Backup branch rotation skipped for '${workingBranch}': ${message}`);
571
+ return { removed: [], warnings };
572
+ }
573
+ const removed = [];
574
+ for (const branch of branches.slice(keep)) {
575
+ try {
576
+ git(repoPath, ['branch', '-D', branch]);
577
+ removed.push(branch);
578
+ }
579
+ catch (err) {
580
+ const message = err instanceof Error ? err.message : String(err);
581
+ warnings.push(`Failed to delete backup branch '${branch}': ${message}`);
582
+ }
462
583
  }
584
+ return { removed, warnings };
463
585
  }
464
586
  /** Abort any in-progress operation, then hard-reset `workingBranch` to a backup branch. */
465
587
  export function restoreBranchFromBackup(repoPath, workingBranch, backupBranch) {
@@ -660,6 +782,13 @@ export function worktreeHasChanges(worktreePath) {
660
782
  return false;
661
783
  }
662
784
  }
785
+ /** Like `worktreeHasChanges`, but propagates the error instead of reporting
786
+ * "clean". Use this on destructive paths: when we cannot determine whether
787
+ * the worktree holds uncommitted work, "unknown" must never be read as
788
+ * "nothing to lose". */
789
+ export function worktreeHasChangesStrict(worktreePath) {
790
+ return git(worktreePath, ['status', '--porcelain']).length > 0;
791
+ }
663
792
  export function getChangedFiles(repoPath, base, includeUntracked = false) {
664
793
  const ref = resolveBase(repoPath, base);
665
794
  const files = [];
@@ -779,6 +908,9 @@ export function getFileAtRef(repoPath, ref, filePath) {
779
908
  return execFileSync('git', ['show', `${resolvedRef}:${filePath}`], {
780
909
  cwd: repoPath,
781
910
  encoding: 'utf-8',
911
+ timeout: SYNC_GIT_TIMEOUT_MS,
912
+ maxBuffer: GIT_MAX_BUFFER_BYTES,
913
+ env: NON_INTERACTIVE_GIT_ENV,
782
914
  });
783
915
  }
784
916
  catch {
@@ -987,6 +1119,25 @@ export function isGitWorktree(repoPath) {
987
1119
  return false;
988
1120
  }
989
1121
  }
1122
+ /**
1123
+ * True when `name` is a branch name Kōbō may hand to git.
1124
+ *
1125
+ * The first character MUST be alphanumeric. Without that anchor a leading `-`
1126
+ * passes any `[A-Za-z0-9/_.-]+` check and git reads the whole argument as an
1127
+ * OPTION — `--force`, `--exec=…` — rather than as a branch. (There is no shell
1128
+ * injection to worry about here: every git call in this project goes through an
1129
+ * argument array. The exposure is option injection.)
1130
+ * The remaining rules mirror `git check-ref-format`.
1131
+ */
1132
+ export function isValidBranchName(name) {
1133
+ if (!/^[A-Za-z0-9][A-Za-z0-9/_.-]*$/.test(name))
1134
+ return false;
1135
+ if (name.includes('..') || name.includes('//'))
1136
+ return false;
1137
+ if (name.endsWith('/') || name.endsWith('.') || name.endsWith('.lock'))
1138
+ return false;
1139
+ return true;
1140
+ }
990
1141
  /**
991
1142
  * Turn an arbitrary string into a git-ref-safe segment, capped to `maxLen`.
992
1143
  * Strips accents (é→e), collapses every run of non-alphanumeric characters
@@ -1165,9 +1316,53 @@ export async function fetchSourceBranchAsync(repoPath, branch, remote = 'origin'
1165
1316
  export function stashPush(repoPath, label) {
1166
1317
  git(repoPath, ['stash', 'push', '--include-untracked', '-m', label]);
1167
1318
  }
1168
- /** Pop the most recent stash entry. */
1169
- export function stashPop(repoPath) {
1170
- git(repoPath, ['stash', 'pop']);
1319
+ /**
1320
+ * Index of the newest stash entry whose message contains `label`, or `null`.
1321
+ * `%gd` is the ref (`stash@{N}`), `%gs` the reflog subject (`On <branch>: <label>`).
1322
+ */
1323
+ export function findStashIndexByLabel(repoPath, label) {
1324
+ let out;
1325
+ try {
1326
+ out = git(repoPath, ['stash', 'list', '--format=%gd%x09%gs']);
1327
+ }
1328
+ catch {
1329
+ return null;
1330
+ }
1331
+ if (!out)
1332
+ return null;
1333
+ for (const line of out.split('\n')) {
1334
+ const separator = line.indexOf('\t');
1335
+ if (separator < 0)
1336
+ continue;
1337
+ const ref = line.slice(0, separator).trim();
1338
+ const subject = line.slice(separator + 1);
1339
+ if (!subject.includes(label))
1340
+ continue;
1341
+ const match = /^stash@\{(\d+)\}$/.exec(ref);
1342
+ if (match)
1343
+ return Number(match[1]);
1344
+ }
1345
+ return null;
1346
+ }
1347
+ /**
1348
+ * Pop a stash entry. With `label`, pops the newest entry whose message contains
1349
+ * it instead of blindly popping `stash@{0}`.
1350
+ *
1351
+ * `stashPush` has always written a label, but nothing ever read it back. The
1352
+ * agent and the integrated terminal both stash routinely before a rebase — do
1353
+ * that between our push and our pop and the bare `git stash pop` restores THEIR
1354
+ * entry, burying the user's work one slot deeper where nothing surfaces it.
1355
+ */
1356
+ export function stashPop(repoPath, label) {
1357
+ if (!label) {
1358
+ git(repoPath, ['stash', 'pop']);
1359
+ return;
1360
+ }
1361
+ const index = findStashIndexByLabel(repoPath, label);
1362
+ if (index === null) {
1363
+ throw new Error(`No stash entry labelled '${label}' to restore`);
1364
+ }
1365
+ git(repoPath, ['stash', 'pop', `stash@{${index}}`]);
1171
1366
  }
1172
1367
  /** Stage every change (tracked + untracked) and commit it. Hooks run normally
1173
1368
  * (no --no-verify), per the project's commit conventions. */
@@ -0,0 +1,45 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ /**
4
+ * One promise chain per shared git directory. Same mechanism as the per-worktree
5
+ * lock in `image-service.ts`, keyed differently: remote refs, packed-refs and
6
+ * the object database live in the COMMON git dir, so two worktrees of the same
7
+ * project contend on the same file locks even though their working directories
8
+ * are disjoint.
9
+ */
10
+ const locks = new Map();
11
+ /**
12
+ * Absolute path of the git directory shared by every worktree of a repository.
13
+ *
14
+ * Falls back to the resolved input path when git cannot answer (not a
15
+ * repository, git missing): a wrong key only costs parallelism, never
16
+ * correctness, whereas throwing here would break every caller.
17
+ */
18
+ export function resolveGitCommonDir(repoPath) {
19
+ try {
20
+ const out = execFileSync('git', ['rev-parse', '--git-common-dir'], {
21
+ cwd: repoPath,
22
+ encoding: 'utf-8',
23
+ timeout: 15_000,
24
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
25
+ }).trim();
26
+ return path.resolve(repoPath, out);
27
+ }
28
+ catch {
29
+ return path.resolve(repoPath);
30
+ }
31
+ }
32
+ /** Run `fn` with exclusive access to `repoPath`'s shared git directory. */
33
+ export function withGitRepoLock(repoPath, fn) {
34
+ const key = resolveGitCommonDir(repoPath);
35
+ const previous = locks.get(key) ?? Promise.resolve();
36
+ // The second argument to .then() means: even if the previous operation in the
37
+ // queue rejected, still run fn — one failure must not block the whole queue.
38
+ const next = previous.then(fn, fn);
39
+ locks.set(key, next.then(() => { }, () => { }));
40
+ return next;
41
+ }
42
+ /** Test-only: drop every queued chain so tests don't inherit each other's state. */
43
+ export function _resetGitRepoLocksForTest() {
44
+ locks.clear();
45
+ }
@@ -0,0 +1,108 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { getKoboHome } from './paths.js';
4
+ const MAX_FILE_BYTES = 5 * 1024 * 1024;
5
+ const KEPT_ROTATIONS = 3;
6
+ const LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
7
+ let cachedDir = null;
8
+ /** Test hook: drop the cached directory so a new KOBO_HOME takes effect. */
9
+ export function _resetLoggerForTest() {
10
+ cachedDir = null;
11
+ }
12
+ function logDir() {
13
+ if (cachedDir === null)
14
+ cachedDir = path.join(getKoboHome(), 'logs');
15
+ return cachedDir;
16
+ }
17
+ export function getLogFilePath() {
18
+ return path.join(logDir(), 'kobo.log');
19
+ }
20
+ function rotateIfNeeded(file) {
21
+ let size = 0;
22
+ try {
23
+ size = fs.statSync(file).size;
24
+ }
25
+ catch {
26
+ return; // no file yet — nothing to rotate
27
+ }
28
+ if (size < MAX_FILE_BYTES)
29
+ return;
30
+ try {
31
+ fs.rmSync(`${file}.${KEPT_ROTATIONS}`, { force: true });
32
+ for (let i = KEPT_ROTATIONS - 1; i >= 1; i--) {
33
+ if (fs.existsSync(`${file}.${i}`))
34
+ fs.renameSync(`${file}.${i}`, `${file}.${i + 1}`);
35
+ }
36
+ fs.renameSync(file, `${file}.1`);
37
+ }
38
+ catch (err) {
39
+ console.error('[logger] rotation failed:', err);
40
+ }
41
+ }
42
+ function mirrorToConsole(entry) {
43
+ const line = `[${entry.scope}] ${entry.message}`;
44
+ if (entry.level === 'error')
45
+ console.error(line, entry.meta ?? '');
46
+ else if (entry.level === 'warn')
47
+ console.warn(line, entry.meta ?? '');
48
+ else
49
+ console.log(line, entry.meta ?? '');
50
+ }
51
+ export function log(level, scope, message, meta) {
52
+ const entry = { ts: new Date().toISOString(), level, scope, message };
53
+ if (meta !== undefined)
54
+ entry.meta = meta;
55
+ mirrorToConsole(entry);
56
+ // A logging failure must NEVER take down the operation it was logging.
57
+ try {
58
+ const dir = logDir();
59
+ fs.mkdirSync(dir, { recursive: true });
60
+ const file = getLogFilePath();
61
+ rotateIfNeeded(file);
62
+ fs.appendFileSync(file, `${JSON.stringify(entry)}\n`, 'utf-8');
63
+ }
64
+ catch {
65
+ // Console already has the line; nothing more to do.
66
+ }
67
+ }
68
+ export function logInfo(scope, message, meta) {
69
+ log('info', scope, message, meta);
70
+ }
71
+ export function logWarn(scope, message, meta) {
72
+ log('warn', scope, message, meta);
73
+ }
74
+ export function logError(scope, message, meta) {
75
+ log('error', scope, message, meta);
76
+ }
77
+ /**
78
+ * Most recent entries first, bounded. Reads only the current file — the
79
+ * rotated ones stay on disk for a human with `grep`, which is the right tool
80
+ * for anything this route cannot answer.
81
+ */
82
+ export function readRecentLogs(options = {}) {
83
+ const limit = Math.min(Math.max(options.limit ?? 200, 1), 2000);
84
+ const minLevel = options.level ? LEVEL_ORDER[options.level] : 0;
85
+ let raw;
86
+ try {
87
+ raw = fs.readFileSync(getLogFilePath(), 'utf-8');
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ const out = [];
93
+ const lines = raw.split('\n');
94
+ for (let i = lines.length - 1; i >= 0 && out.length < limit; i--) {
95
+ const line = lines[i].trim();
96
+ if (line.length === 0)
97
+ continue;
98
+ try {
99
+ const entry = JSON.parse(line);
100
+ if (LEVEL_ORDER[entry.level] >= minLevel)
101
+ out.push(entry);
102
+ }
103
+ catch {
104
+ // A truncated last line (process killed mid-append) is skipped, not fatal.
105
+ }
106
+ }
107
+ return out;
108
+ }
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import * as wsService from '../services/websocket-service.js';
5
+ import { getIndexLockPath } from './git-ops.js';
5
6
  /** Default wall-clock budget for a user script before it is force-killed. */
6
7
  export const SCRIPT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
7
8
  /**
@@ -52,6 +53,16 @@ export function runScript(opts) {
52
53
  wsService.emit(workspaceId, `${eventPrefix}:output`, {
53
54
  text: `[kobo] Script timed out after ${Math.round(timeoutMs / 60000)} minutes`,
54
55
  });
56
+ // SIGKILL on the process group gives a running `git commit` no chance to
57
+ // release its index lock. Name the file here, while the user is looking
58
+ // at this feed — otherwise the next git write fails with a raw git error
59
+ // whose cause is three screens above.
60
+ const lockPath = getIndexLockPath(worktreePath);
61
+ if (lockPath) {
62
+ wsService.emit(workspaceId, `${eventPrefix}:output`, {
63
+ text: `[kobo] A git index lock was left behind at ${lockPath}. Remove it before the next git command: rm -f '${lockPath}'`,
64
+ });
65
+ }
55
66
  }, timeoutMs);
56
67
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*[a-zA-Z]`, 'g');
57
68
  const stripAnsi = (s) => s.replace(ansiPattern, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loicngr/kobo",
3
- "version": "1.11.16",
3
+ "version": "1.11.17",
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{n as e}from"./vue-i18n-C3W5i7h6.js";import{B as t,C as n,St as r,wt as i,yt as a}from"./reactivity.esm-bundler-DtSZKRQB.js";import{$ as o,A as s,At as c,B as l,Dt as u,Et as d,H as f,It as p,Mt as m,Ot as h,Rt as g,Tt as _,U as v,V as y,W as b,Wt as x,X as S,Z as C,_n as w,fn as T,st as E,t as D,ut as O,w as k}from"./runtime-dom.esm-bundler-DgevF6KV.js";import{S as A,W as j,Z as M,b as N,l as P,t as ee,w as te}from"./QBtn-B7WFdfmy.js";import{O as ne,c as F,d as I,k as re}from"./use-id-DlouqdB8.js";import{H as ie,T as ae,h as oe,lt as L,m as R,w as se}from"./index-Dv1p-1sA.js";import{t as ce}from"./QSpinnerDots-DWOFEezR.js";import{t as z}from"./QList-Ccqhimcb.js";import{i as B,r as V}from"./QSelect-D9bHHxAA.js";import{t as le}from"./QExpansionItem-C8CWo-aP.js";import{r as ue,t as H}from"./pending-diff-open-Cia8sndE.js";import{t as U}from"./documents-B6eOZeFK.js";import{a as W,i as G}from"./layout-nWIyHeJf.js";import{i as de,n as fe,o as pe,r as me,t as K}from"./render-chat-markdown-B6sa44Xr.js";import{a as he,i as ge,n as _e,o as ve,r as ye,s as be}from"./WorkspacePage-BVxtefbj.js";var q=[`horizontal`,`vertical`,`cell`,`none`],J=M({name:`QMarkupTable`,props:{...re,dense:Boolean,flat:Boolean,bordered:Boolean,square:Boolean,wrapCells:Boolean,separator:{type:String,default:`horizontal`,validator:e=>q.includes(e)}},setup(e,{slots:t}){let n=E(),r=ne(e,n.proxy.$q),i=l(()=>`q-markup-table q-table__container q-table__card q-table--${e.separator}-separator`+(r.value?` q-table--dark q-table__card--dark q-dark`:``)+(e.dense?` q-table--dense`:``)+(e.flat?` q-table--flat`:``)+(e.bordered?` q-table--bordered`:``)+(e.square?` q-table--square`:``)+(e.wrapCells?``:` q-table--no-wrap`));return()=>O(`div`,{class:i.value},[O(`table`,{class:`q-table`},A(t.default))])}});function xe(e,t){return O(`div`,e,[O(`table`,{class:`q-table`},t)])}var Se={list:z,table:J},Ce=[`list`,`table`,`__qtable`],we=M({name:`QVirtualScroll`,props:{...B,type:{type:String,default:`list`,validator:e=>Ce.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:I},setup(e,{slots:t,attrs:r}){let i,a=n(null),o=l(()=>e.itemsSize>=0&&e.itemsFn!==void 0?Number.parseInt(e.itemsSize,10):Array.isArray(e.items)?e.items.length:0),{virtualScrollSliceRange:s,localResetVirtualScroll:f,padVirtualScroll:p,onVirtualScrollEvt:g}=V({virtualScrollLength:o,getVirtualScrollTarget:x,getVirtualScrollEl:b}),_=l(()=>{if(o.value===0)return[];let t=(e,t)=>({index:s.value.from+t,item:e});return e.itemsFn===void 0?e.items.slice(s.value.from,s.value.to).map(t):e.itemsFn(s.value.from,s.value.to-s.value.from).map(t)}),v=l(()=>`q-virtual-scroll q-virtual-scroll`+(e.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(e.scrollTarget===void 0?` scroll`:``)),y=l(()=>e.scrollTarget===void 0?{tabindex:0}:{});T(o,()=>{f()}),T(()=>e.scrollTarget,()=>{C(),S()});function b(){let e=a.value;return e===null?null:e.$el||e}function x(){return i}function S(){let t=b();t!==null&&(i=F(t,e.scrollTarget),i.addEventListener(`scroll`,g,j.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,g,j.passive),i=void 0)}function w(){let n=p(e.type===`list`?`div`:`tbody`,_.value.map(t.default));return t.before!==void 0&&(n=t.before().concat(n)),N(t.after,n)}return u(()=>{f()}),m(()=>{S()}),d(()=>{S()}),c(()=>{C()}),h(()=>{C()}),()=>{if(t.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return e.type===`__qtable`?xe({ref:a,class:`q-table__middle `+v.value},w()):O(Se[e.type],{...r,ref:a,class:[r.class,v.value],...y.value},w)}}}),Te=new Set([`setup`,`cleanup`,`archive`]);function Ee(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:Te.has(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function De(e){let t=[],n=null,r=null;for(let i of e){let e=Ee(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}function Y(e){switch(e.type){case`text`:return`text:${e.messageId}`;case`thinking`:return`thinking:${e.messageId}:${e.eventIds?.[0]??e.ts??``}`;case`tool`:return`tool:${e.toolCallId}`;case`session`:return`session:${e.kind}:${e.eventIds?.[0]??e.ts??``}`;case`user`:return`user:${e.eventIds?.[0]??`${e.sender}:${e.ts??``}`}`;case`error`:return`error:${e.eventIds?.[0]??`${e.category}:${e.ts??``}`}`}}function Oe(e){let t=e.items[0];return`${e.speaker}:${t?Y(t):`empty`}`}function ke(e,t){let n=Math.min(t,e.length)-1;for(let t=n;t>=0;t--)if(e[t]?.speaker===`user`)return t;return-1}function Ae(e,t,n){return new Promise(r=>{let i=!1,a=null,o=null,s=e=>{i||(i=!0,a!==null&&clearTimeout(a),a=null,o?.(),r(e))};if(o=T(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var je={class:`agent-error-item`},X={class:`agent-error-item__body`},Me={class:`agent-error-item__title`},Z={class:`agent-error-item__message`},Ne=G(o({__name:`AgentErrorItem`,props:{item:{}},setup(t){let n=t,{te:r}=e(),a=l(()=>{let e=`agent.error.${n.item.category}`;return r(e)?e:`agent.error.other`});return(e,n)=>(g(),b(`div`,je,[C(P,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),y(`div`,X,[y(`div`,Me,i(e.$t(a.value)),1),y(`div`,Z,i(t.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Q={class:`text-caption text-kobo-3`},$=o({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=l(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return be(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(g(),b(`span`,Q,i(e.$t(n.value)),1))}});function Pe(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){Fe(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 Fe(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=Ie(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 Ie(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`],Re=G(o({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=U(),r=R(),i=l(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),a=l(()=>{let e=t.item;if(e.streaming)return K(e.text);let n=i.value;return me(`${e.messageId}|${n.join(`,`)}`,e.text,e=>{let t=Pe(pe.parse(e,{async:!1,breaks:!0,gfm:!0}),n);return de(t,{addAttr:[`data-document-path`]})})});function o(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let i=t.getAttribute(`data-document-path`),a=r.selectedWorkspaceId;!i||!a||n.openDocumentByPath(a,i)}return(t,n)=>(g(),b(`div`,{class:`markdown-message`,onClick:o},[y(`div`,{innerHTML:a.value},null,8,Le),e.item.streaming?(g(),f(te,{key:0,size:`xs`,class:`q-ml-xs`})):v(``,!0)]))}}),[[`__scopeId`,`data-v-539815f6`]]);function ze(e,t){let n=e.split(`
2
+ `),r=t.split(`
3
+ `),i=0;for(;i<n.length&&i<r.length&&n[i]===r[i];)i++;let a=n.length,o=r.length;for(;a>i&&o>i&&n[a-1]===r[o-1];)a--,o--;let s=n.slice(0,i).map(e=>({type:`context`,content:e})),c=n.slice(a).map(e=>({type:`context`,content:e})),l=n.slice(i,a),u=r.slice(i,o);return l.length>800||u.length>800?[...s,...l.map(e=>({type:`del`,content:e})),...u.map(e=>({type:`add`,content:e})),...c]:[...s,...Be(l,u),...c]}function Be(e,t){let n=e.length,r=t.length;if(n===0&&r===0)return[];if(n===0)return t.map(e=>({type:`add`,content:e}));if(r===0)return e.map(e=>({type:`del`,content:e}));let i=r+1,a=new Int32Array((n+1)*i);for(let o=n-1;o>=0;o--)for(let n=r-1;n>=0;n--)a[o*i+n]=e[o]===t[n]?a[(o+1)*i+(n+1)]+1:Math.max(a[(o+1)*i+n],a[o*i+(n+1)]);let o=[],s=0,c=0;for(;s<n&&c<r;)e[s]===t[c]?(o.push({type:`context`,content:e[s]}),s++,c++):a[(s+1)*i+c]>=a[s*i+(c+1)]?(o.push({type:`del`,content:e[s]}),s++):(o.push({type:`add`,content:t[c]}),c++);for(;s<n;)o.push({type:`del`,content:e[s++]});for(;c<r;)o.push({type:`add`,content:t[c++]});return o}function Ve(e){let t=e.split(`
4
+ `),n=[],r=!1;for(let e of t){if(e.startsWith(`@@`)){r=!0;continue}!r&&(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 He(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=Ve(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 Ue(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=We(e,n);if(t!==e)return t}let r=We(e,`${t.projectPath}/${ae}/${t.workingBranch}`);return r===e?We(e,t.projectPath):r}function We(e,t){if(!t)return e;let n=Ge(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function Ge(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(Ke).join(`[\\\\/]+`)}`:``}function Ke(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var qe={class:`tool-name`},Je=[`title`],Ye={key:0,class:`tool-stat-add`},Xe={key:1,class:`tool-stat-del`},Ze={class:`diff-sign`},Qe={class:`tool-name`},$e=[`title`],et=G(o({__name:`ToolCallItem`,props:{item:{}},setup(r){let o=r,c=n(!1),u=R(),{t:d}=e(),p=l(()=>He(o.item.name,o.item.input)),m=l(()=>p.value?Ue(p.value.filePath,u.selectedWorkspace):``),h={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`,TaskCreate:`add_task`,TaskUpdate:`checklist`,TaskGet:`checklist`,TaskList:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},_=l(()=>h[o.item.name]??`build`),E=l(()=>{if(p.value)return``;let e=o.item.input,t=D(e);return t?Ue(t,u.selectedWorkspace):``});function D(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 O=l(()=>{let e=p.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?ze(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}),A=l(()=>{let e=o.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),j=new Set([`Read`]),M=l(()=>!!(o.item.result&&A.value)&&(!j.has(o.item.name)||o.item.result?.isError===!0));function N(){c.value=!c.value}function te(){let e=p.value?.filePath,t=u.selectedWorkspace,n=t?.id,r=e?Ue(e,t):``;!r||/^(?:[A-Za-z]:)?[\\/]/.test(r)||!n||(H(n,r),window.dispatchEvent(new CustomEvent(`kobo:open-diff`,{detail:{workspaceId:n,path:r}})))}return T(()=>o.item.result?.isError===!0,e=>{e&&(c.value=!0)},{immediate:!0}),(e,n)=>p.value?(g(),b(`div`,{key:0,class:a([`tool-row`,{"tool-row-expanded":c.value}])},[y(`div`,{class:`tool-header`,onClick:N},[C(P,{name:_.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),y(`span`,qe,i(p.value.toolName===`Bash:rm`?`Bash`:p.value.toolName),1),y(`span`,{class:`tool-path`,title:p.value.filePath},i(m.value),9,Je),C(ee,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:k(te,[`stop`])},{default:w(()=>[C(W,null,{default:w(()=>[S(i(t(d)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),p.value.additions>0?(g(),b(`span`,Ye,`+`+i(p.value.additions),1)):v(``,!0),p.value.deletions>0?(g(),b(`span`,Xe,`-`+i(p.value.deletions),1)):v(``,!0),r.item.result?.isError?(g(),f(P,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):r.item.result?(g(),f(P,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):v(``,!0),C(P,{name:c.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-kobo-3`},null,8,[`name`])]),c.value&&O.value?(g(),b(`div`,{key:0,class:`tool-diff`,onClick:n[0]||=k(()=>{},[`stop`])},[(g(!0),b(s,null,x(O.value,(e,t)=>(g(),b(`div`,{key:t,class:a([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[y(`span`,Ze,i(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),S(i(e.content),1)],2))),128))])):v(``,!0)],2)):(g(),b(`div`,{key:1,class:a([`tool-row tool-row-generic`,{"tool-row-expanded":c.value,"tool-row--toggleable":M.value}])},[y(`div`,{class:`tool-header`,onClick:n[1]||=e=>M.value&&N()},[C(P,{name:_.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),y(`span`,Qe,i(r.item.name),1),E.value?(g(),b(`span`,{key:0,class:`tool-arg`,title:D(r.item.input)||E.value},i(E.value),9,$e)):v(``,!0),r.item.result?.isError?(g(),f(P,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):r.item.result?(g(),f(P,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):v(``,!0),M.value?(g(),f(P,{key:3,name:c.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-kobo-3`},null,8,[`name`])):v(``,!0)]),c.value&&M.value?(g(),b(`div`,{key:0,class:`tool-output`,onClick:n[2]||=k(()=>{},[`stop`])},i(A.value),1)):v(``,!0)],2))}}),[[`__scopeId`,`data-v-2482adfe`]]);function tt(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 nt=[`innerHTML`],rt=[`innerHTML`],it=[`src`],at=G(o({__name:`UserMessageItem`,props:{item:{}},setup(e){let t=e,r=R(),i=l(()=>t.item.sender===`system-prompt`),a=l(()=>{let e=tt(t.item.content,r.selectedWorkspaceId??``);return fe(e)}),o=n(null),c=n(!1);function u(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(o.value=n.src,c.value=!0)}return(e,t)=>(g(),b(s,null,[i.value?(g(),f(le,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:w(()=>[y(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:a.value},null,8,nt)]),_:1},8,[`label`])):(g(),b(`div`,{key:1,class:`markdown-message`,onClick:u},[y(`div`,{innerHTML:a.value},null,8,rt)])),C(L,{modelValue:c.value,"onUpdate:modelValue":t[1]||=e=>c.value=e},{default:w(()=>[o.value?(g(),b(`img`,{key:0,src:o.value,alt:``,class:`image-lightbox-img`,onClick:t[0]||=e=>c.value=!1},null,8,it)):v(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-7687eb7e`]]),ot=[`data-event-ids`],st={class:`turn-header`},ct={key:0,class:`turn-time`},lt={class:`turn-time turn-time-updated`},ut={key:2,class:`turn-actions`},dt={class:`turn-body`},ft={key:0,class:`turn-scroll-top`},pt=G(o({__name:`TurnCard`,props:{turn:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(o,{emit:c}){let u=o,d=c,{t:p}=e(),m=n(null);function h(){let e=m.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;d(`scrollTo`,Math.max(0,n-8))}let _=l(()=>{switch(u.turn.speaker){case`user`:return{label:p(`chat.you`),accent:`var(--kobo-text)`,badgeClass:`turn-badge-user`};case`agent`:return{label:p(`chat.agent`),accent:`var(--kobo-text-2)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:p(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:p(`chat.session`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-session`};case`script`:{let e=u.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:p(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});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 E=l(()=>T(u.turn.ts)),D=l(()=>u.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),O=l(()=>{let e=u.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=l(()=>{let e=u.turn.ts,t=O.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=l(()=>k.value!==``),j=l(()=>u.turn.items.filter(e=>e.type===`tool`).length);return(e,n)=>(g(),b(`div`,{ref_key:`cardEl`,ref:m,class:a([`turn-card`,{"turn-card--user":o.turn.speaker===`user`,"turn-card--highlighted":o.highlighted}]),"data-event-ids":D.value,style:r({"--turn-accent":_.value.accent})},[y(`div`,st,[y(`span`,{class:a([`turn-badge`,_.value.badgeClass])},i(_.value.label),3),E.value?(g(),b(`span`,ct,i(E.value),1)):v(``,!0),A.value?(g(),b(s,{key:1},[C(P,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),y(`span`,lt,[S(i(k.value)+` `,1),C(W,null,{default:w(()=>[S(i(t(p)(`chat.lastUpdatedAt`,{time:k.value})),1)]),_:1})])],64)):v(``,!0),j.value>0?(g(),b(`span`,ut,` · `+i(t(p)(`chat.nActions`,{n:j.value})),1)):v(``,!0)]),y(`div`,dt,[(g(!0),b(s,null,x(o.turn.items,e=>(g(),b(s,{key:t(Y)(e)},[e.type===`text`?(g(),f(Re,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(g(),f(_e,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(g(),f(et,{key:2,item:e},null,8,[`item`])):e.type===`user`?(g(),f(at,{key:3,item:e},null,8,[`item`])):e.type===`session`?(g(),f($,{key:4,item:e},null,8,[`item`])):e.type===`error`?(g(),f(Ne,{key:5,item:e},null,8,[`item`])):v(``,!0)],64))),128))]),o.turn.items.length>4?(g(),b(`div`,ft,[C(ee,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:h},{default:w(()=>[C(W,null,{default:w(()=>[S(i(t(p)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,ot))}}),[[`__scopeId`,`data-v-408bbac6`]]),mt={key:0,class:`activity-feed-switching`},ht={key:1,class:`activity-feed-wrap`},gt=[`aria-label`],_t={key:0,class:`activity-feed-compacting`},vt={class:`text-caption text-kobo-2`},yt={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},bt={key:1,class:`activity-feed-empty q-pa-md`},xt={class:`activity-feed-empty__title`},St={class:`activity-feed-empty__hint`},Ct={key:2,class:`q-px-md q-pb-md`},wt={class:`activity-feed-nav-cluster`},Tt=300,Et=60,Dt=200,Ot=200,kt=400,At=200,jt=180,Mt=1200,Nt=G(o({__name:`ActivityFeed`,props:{workspaceId:{}},setup(r){let{t:a}=e(),o=r,c=ie(),u=se(),d=R(),h=l(()=>c.isCompacting(o.workspaceId)),E=l(()=>d.selectedSessionId),O=l(()=>d.sessions.find(e=>e.id===E.value)?.engineSessionId??null),k=l(()=>{let e=d.sessions;return e.length!==0&&E.value===e[e.length-1].id});function A(e){return E.value?e?e===E.value||e===O.value:k.value:!0}let j=l(()=>(d.activityFeeds[o.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&A(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),M=l(()=>{let e=d.workspaces.find(e=>e.id===o.workspaceId);return oe(e?.status)}),N=ye(),ne=``,F=l(()=>{let e=c.eventsFor(o.workspaceId),t=c.timestampsFor(o.workspaceId),n=c.sessionIdsFor(o.workspaceId),r=c.eventIdsFor(o.workspaceId),i=[],a=[],s=[];for(let o=0;o<e.length;o++)A(n[o])&&(i.push(e[o]),a.push(t[o]),s.push(r[o]??null));let l=`${o.workspaceId}::${E.value??`*`}`;l!==ne&&(N=ye(),ne=l);let d=ge(N,i,a,M.value,s);return De(ve(d,j.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?u.showVerboseSystemMessages||!he(e.detail):u.showVerboseSystemMessages:!0))}),I=l(()=>u.showVerboseSystemMessages?c.eventsFor(o.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),re=l(()=>{for(let e=F.value.length-1;e>=0;e--){let t=F.value[e]?.items??[];for(let e=t.length-1;e>=0;e--){let n=t[e];if(n){if(n.type===`text`&&!n.streaming)return{key:`text:${n.messageId}`,text:n.text};if(n.type===`tool`)return n.result?{key:`tool:${n.toolCallId}:end`,text:n.result.isError?a(`activity.a11y.toolFailed`,{name:n.name}):a(`activity.a11y.toolFinished`,{name:n.name})}:{key:`tool:${n.toolCallId}:start`,text:a(`activity.a11y.toolStarted`,{name:n.name})}}}}return null}),ae=n(``);T(re,(e,t)=>{if(!e||e.key===t?.key)return;let n=e.key.startsWith(`text:`)?a(`activity.a11y.agentSaid`,{text:e.text.slice(0,Tt)}):e.text;ae.value=n});let L=n(null),z=n(!0),B=n(!1),V=n(null),H=!1,U=n(!0),W=n(new Map);function G(e){let t=e.verticalSize-e.verticalPosition-e.verticalContainerSize;z.value=t<=Et,H&&e.verticalPosition<=Dt&&!B.value&&fe()&&_e()}function de(e,t){return`${e}:${t}`}function fe(){let e=E.value;return e?W.value.get(de(o.workspaceId,e))??!0:c.hasMoreOlderFor(o.workspaceId)}function pe(e,t,n){W.value.set(de(e,t),n)}function me(e){if(!E.value)return c.oldestIdFor(e);let t=c.eventIdsFor(e),n=c.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!A(n[e]))continue;let r=t[e];if(r)return r}}let K=null;function _e(){return K||(K=be().finally(()=>{K=null}),K)}async function be(){let e=o.workspaceId,t=E.value,n=me(e);if(!n)return;B.value=!0;let r=Date.now();try{let r=L.value;await _();let i=r?.getScroll().verticalSize??0,a=r?.getScroll().verticalPosition??0,o=new URLSearchParams({before:n,limit:`200`});t&&o.set(`session`,t);let s=fetch(`/api/workspaces/${e}/events?${o.toString()}`),l=new Promise(e=>setTimeout(e,Ot)),[u]=await Promise.all([s,l]);if(!u.ok){t?pe(e,t,!1):c.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let f=await u.json(),p=f.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),v=m.map(e=>e.createdAt),y=m.map(e=>e.sessionId??null),b=m.map(e=>e.id),x=p.length>0?p[0].id:n;t&&pe(e,t,f.hasMore),c.prepend(e,g,v,{oldestId:x,hasMoreOlder:t?c.hasMoreOlderFor(e):f.hasMore,sessionIds:y,eventIds:b});for(let t of h){let n=t.payload;typeof n.content==`string`&&d.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 _(),r){let e=r.getScroll().verticalSize-i;if(e>0){let t=Math.max(a+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-r,t=Math.max(0,Ot-e);await new Promise(e=>setTimeout(e,t+kt)),B.value=!1}}async function q(e=0){await _();let t=L.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let J=null,xe=0;function Se(){if(J!=null)return;let e=performance.now()-xe<jt;J=requestAnimationFrame(()=>{J=null,xe=performance.now(),q(e?0:180)})}function Ce(e){let t=L.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Te(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function Ee(e){let t=e.detail;if(!(t?.workspaceId!==o.workspaceId||!t.eventId))try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let n=await fetch(`/api/workspaces/${o.workspaceId}/events?${e}`);if(!n.ok)return;let r=await n.json(),i=r.events??[],a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===o.workspaceId),s=i.filter(e=>e.type===`user:message`&&e.workspaceId===o.workspaceId);c.reset(o.workspaceId,a.map(e=>e.payload),a.map(e=>e.createdAt),{oldestId:i[0]?.id,hasMoreOlder:r.hasMore??!1,sessionIds:a.map(e=>e.sessionId),eventIds:a.map(e=>e.id)});for(let e of s){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(o.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await Ae(U,e=>!e,5e3),await _(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e())));let l=F.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(l<0)return;V.value=t.eventId,Y.value?.scrollTo(l,`center`),X.value=l,window.setTimeout(()=>{V.value===t.eventId&&(V.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=n(null),je=n(null),X=n(0);function Me(e){X.value=e.index}let Z=n(!1);async function Ne(){if(Z.value)return;Z.value=!0;let e=o.workspaceId;try{let t=ke(F.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==o.workspaceId)return;if(!fe())break;let n=F.value.length;if(await _e(),await _(),e!==o.workspaceId)return;if(X.value+=F.value.length-n,t=ke(F.value,X.value),t>=0)break}t>=0&&e===o.workspaceId&&(Y.value?.scrollTo(t,`start`),X.value=t)}finally{Z.value=!1}}async function Q(){H=!1,await _(),await q(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{H=!0})})}let $=l(()=>{let e=c.sessionIdsFor(o.workspaceId);if(!E.value)return e.length;let t=0;for(let n of e)A(n)&&t++;return t}),Pe=l(()=>c.eventsFor(o.workspaceId).length);async function Fe(){U.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,At));let t=Math.max(0,e+Mt-Date.now());await Ae(Pe,e=>e>0,t),U.value=!1}T(U,async e=>{!e&&$.value>0&&await Q(),!e&&$.value===0&&E.value&&Re(),e||_(()=>{je.value=L.value?.getScrollTarget()??null})}),m(()=>{window.addEventListener(`kobo:focus-history-event`,Ee),_(()=>{je.value=L.value?.getScrollTarget()??null}),Fe(),$.value>0&&Q(),E.value&&Re();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(Ee(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:o.workspaceId,sessionId:E.value,eventId:e}})),Te())});let Ie=$.value>0;T($,async(e,t)=>{if(!B.value){if(!Ie&&e>0){Ie=!0,await Q();return}e>t&&z.value&&!B.value&&Se()}}),p(()=>{window.removeEventListener(`kobo:focus-history-event`,Ee),J!=null&&(cancelAnimationFrame(J),J=null)}),T(()=>o.workspaceId,()=>{z.value=!0,Ie=$.value>0,H=!1,Fe(),$.value>0&&Q()}),T(()=>d.selectedSessionId,async()=>{z.value=!0,H=!1,await Q(),Re()});let Le=new Set;async function Re(){let e=E.value;if(!e||$.value>0)return;let t=de(o.workspaceId,e);if(!Le.has(t)){Le.add(t);try{let t=await fetch(`/api/workspaces/${o.workspaceId}/events?session=${encodeURIComponent(e)}&limit=500`);if(!t.ok)return;let n=await t.json(),r=n.events??[];if(r.length===0)return;let i=r.filter(e=>e.type===`agent:event`&&e.workspaceId===o.workspaceId),a=r.filter(e=>e.type===`user:message`&&e.workspaceId===o.workspaceId),s=i.map(e=>e.payload),l=i.map(e=>e.createdAt),u=i.map(e=>e.sessionId??null),f=i.map(e=>e.id);pe(o.workspaceId,e,n.hasMore),s.length>0&&c.prepend(o.workspaceId,s,l,{oldestId:r[0].id,hasMoreOlder:c.hasMoreOlderFor(o.workspaceId),sessionIds:u,eventIds:f});for(let e of a){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(o.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await _(),await q(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Le.delete(t)}}}let ze=l(()=>j.value.filter(e=>e.sender!==`system-prompt`).length);T(ze,async(e,t)=>{e>t&&(z.value=!0,await q(180))});async function Be(){z.value=!0,await q(250)}return(e,n)=>U.value?(g(),b(`div`,mt,[C(ce,{size:`40px`,color:`primary`})])):(g(),b(`div`,ht,[y(`div`,{"data-testid":`activity-live-region`,class:`sr-only`,role:`log`,"aria-live":`polite`,"aria-atomic":`true`,"aria-label":e.$t(`activity.a11y.region`)},i(ae.value),9,gt),C(D,{name:`fade`},{default:w(()=>[h.value?(g(),b(`div`,_t,[C(ce,{size:`18px`,color:`primary`}),y(`span`,vt,i(e.$t(`activity.compacting`)),1)])):v(``,!0)]),_:1}),C(ue,{ref_key:`scrollRef`,ref:L,class:`activity-feed-scroll`,onScroll:G},{default:w(()=>[B.value?(g(),b(`div`,yt,[C(te,{size:`sm`}),S(` `+i(e.$t(`activity.loading_older`)),1)])):v(``,!0),C(we,{ref_key:`virtualScrollRef`,ref:Y,class:`q-pa-md`,items:F.value,"scroll-target":je.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Me},{default:w(({item:e,index:n})=>[(g(),f(pt,{key:t(Oe)(e),turn:e,"data-turn-index":n,highlighted:e.items.some(e=>e.eventIds?.includes(V.value??``)??!1),onScrollTo:Ce},null,8,[`turn`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),F.value.length===0&&I.value.length===0&&!B.value?(g(),b(`div`,bt,[C(P,{name:`forum`,size:`28px`}),y(`div`,xt,i(e.$t(`activity.empty`)),1),y(`div`,St,i(e.$t(`activity.emptyHint`)),1)])):v(``,!0),I.value.length?(g(),b(`div`,Ct,[C(le,{label:e.$t(`activity.raw_lines`,{n:I.value.length}),dense:``},{default:w(()=>[(g(!0),b(s,null,x(I.value,(e,t)=>(g(),b(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},i(e),1))),128))]),_:1},8,[`label`])])):v(``,!0)]),_:1},512),y(`div`,wt,[z.value?v(``,!0):(g(),f(ee,{key:0,round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),"aria-label":e.$t(`activity.scroll_to_bottom`),onClick:Be},null,8,[`title`,`aria-label`])),C(ee,{round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),"aria-label":e.$t(`activity.prev_user_message`),loading:Z.value,disable:Z.value,onClick:Ne},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-2323ca85`]]);export{Nt as default};
@@ -0,0 +1 @@
1
+ .agent-error-item[data-v-e98c3c02]{gap:var(--kobo-space-sm);padding:var(--kobo-space-sm) var(--kobo-space-md);border-left:2px solid var(--kobo-danger);background:var(--kobo-surface-2);border-radius:var(--kobo-radius-sm);align-items:flex-start;display:flex}.agent-error-item__icon[data-v-e98c3c02]{color:var(--kobo-danger);flex:none;margin-top:2px}.agent-error-item__body[data-v-e98c3c02]{min-width:0}.agent-error-item__title[data-v-e98c3c02]{color:var(--kobo-text);font-weight:600}.agent-error-item__message[data-v-e98c3c02]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;word-break:break-word;font-size:12px}.markdown-message[data-v-539815f6]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-539815f6] *{max-width:100%}.markdown-message[data-v-539815f6] p{margin:0 0 .5em}.markdown-message[data-v-539815f6] p:last-child{margin-bottom:0}.markdown-message[data-v-539815f6] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-539815f6] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-539815f6] pre code{background:0 0;padding:0}.markdown-message[data-v-539815f6] ul,.markdown-message[data-v-539815f6] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-539815f6] li{margin:.15em 0}.markdown-message[data-v-539815f6] a{color:var(--kobo-text-2);text-decoration:underline}.markdown-message[data-v-539815f6] .document-link{color:var(--kobo-text-2);cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-539815f6] .document-link:hover{color:var(--kobo-text);-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-539815f6] h1,.markdown-message[data-v-539815f6] h2,.markdown-message[data-v-539815f6] h3,.markdown-message[data-v-539815f6] h4,.markdown-message[data-v-539815f6] h5,.markdown-message[data-v-539815f6] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-539815f6] h1{font-size:1.25em}.markdown-message[data-v-539815f6] h2{font-size:1.15em}.markdown-message[data-v-539815f6] h3{font-size:1.08em}.markdown-message[data-v-539815f6] h4,.markdown-message[data-v-539815f6] h5,.markdown-message[data-v-539815f6] h6{font-size:1em}.markdown-message[data-v-539815f6] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-539815f6] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-539815f6] th,.markdown-message[data-v-539815f6] td{border:1px solid #ffffff26;padding:.25em .5em}.tool-row[data-v-2482adfe]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-2482adfe]{color:var(--kobo-text-2);cursor:default;align-items:center;gap:10px;min-width:0;padding:5px 10px;display:flex}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe],.tool-row--toggleable .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe]:hover,.tool-row--toggleable .tool-header[data-v-2482adfe]:hover{background:#ffffff08}.tool-icon[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0}.tool-name[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0;font-weight:600}.tool-arg[data-v-2482adfe],.tool-path[data-v-2482adfe]{color:var(--kobo-text-3);text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:var(--kobo-font-mono);font-size:11.5px;overflow:hidden}.tool-open-diff[data-v-2482adfe]{flex-shrink:0}.tool-path[data-v-2482adfe],.tool-arg[data-v-2482adfe]{flex:1}.tool-stat-add[data-v-2482adfe]{color:var(--kobo-success);flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-2482adfe]{color:var(--kobo-danger);flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-2482adfe]{max-height:400px;font-family:var(--kobo-font-mono);background:#0003;border-radius:4px;margin-top:4px;padding:8px 0;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-2482adfe]{white-space:pre;color:var(--kobo-text-2);padding:0 12px}.diff-sign[data-v-2482adfe]{width:14px;color:var(--kobo-text-disabled);-webkit-user-select:none;user-select:none;display:inline-block}.diff-add[data-v-2482adfe]{color:var(--kobo-success);background:#66bb6a1a}.diff-add .diff-sign[data-v-2482adfe]{color:var(--kobo-success)}.diff-del[data-v-2482adfe]{color:var(--kobo-danger);background:#ef53501a}.diff-del .diff-sign[data-v-2482adfe]{color:var(--kobo-danger)}.tool-output[data-v-2482adfe]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-size:11px;overflow:auto}.markdown-message[data-v-7687eb7e]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-7687eb7e] *{max-width:100%}.markdown-message[data-v-7687eb7e] img{-o-object-fit:contain;object-fit:contain;cursor:zoom-in;background:#0003;border-radius:4px;max-width:180px;max-height:100px;margin:.3em 0;display:block}.markdown-message[data-v-7687eb7e] code{word-break:break-all}.markdown-message[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-message[data-v-7687eb7e] p:last-child{margin-bottom:0}.markdown-message[data-v-7687eb7e] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-7687eb7e] h1,.markdown-message[data-v-7687eb7e] h2,.markdown-message[data-v-7687eb7e] h3,.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-7687eb7e] h1{font-size:1.25em}.markdown-message[data-v-7687eb7e] h2{font-size:1.15em}.markdown-message[data-v-7687eb7e] h3{font-size:1.08em}.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{font-size:1em}.markdown-user-prompt[data-v-7687eb7e]{color:var(--kobo-text-2);font-size:12px;font-style:italic}.markdown-user-prompt[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-user-prompt[data-v-7687eb7e] code{background:#ffffff14;border-radius:3px;padding:.1em .3em;font-style:normal}.image-lightbox-img{-o-object-fit:contain;object-fit:contain;cursor:zoom-out;background:#0000004d;border-radius:4px;max-width:92vw;max-height:92vh;display:block}.turn-card[data-v-408bbac6]{border:1px solid #ffffff14;border-left:3px solid var(--turn-accent);background:#ffffff05;border-radius:6px;min-width:0;max-width:100%;margin:14px 0;overflow:hidden}.turn-card--highlighted[data-v-408bbac6]{animation:1.8s ease-out search-highlight-408bbac6}@keyframes search-highlight-408bbac6{0%,45%{box-shadow:0 0 0 2px #818cf8f2,0 0 22px #818cf873}to{box-shadow:none}}.turn-header[data-v-408bbac6]{color:var(--kobo-text-3);background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-408bbac6]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-408bbac6]{color:var(--kobo-text);background:#ce93d826}.turn-badge-agent[data-v-408bbac6]{color:var(--kobo-text-2);background:#7986cb26}.turn-badge-system[data-v-408bbac6]{color:var(--kobo-text-2);background:#75757533;font-style:italic}.turn-badge-session[data-v-408bbac6]{color:var(--kobo-text-3);background:#61616133}.turn-badge-script[data-v-408bbac6]{color:var(--kobo-success);background:#4db6ac26}.turn-time[data-v-408bbac6]{color:var(--kobo-text-disabled);font-family:var(--kobo-font-mono);font-size:11px}.turn-time-arrow[data-v-408bbac6]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-408bbac6]{color:var(--kobo-text-3)}.turn-actions[data-v-408bbac6]{color:var(--kobo-text-3);font-size:11px}.turn-body[data-v-408bbac6]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-408bbac6]>*{min-width:0;max-width:100%}.turn-body[data-v-408bbac6] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-408bbac6]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-408bbac6]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-408bbac6]:hover{opacity:1}.activity-feed-wrap[data-v-2323ca85]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-2323ca85]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-2323ca85]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-compacting[data-v-2323ca85]{z-index:3;background:var(--kobo-surface);border:1px solid var(--kobo-border);border-radius:6px;align-items:center;gap:8px;padding:6px 14px;display:flex;position:absolute;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 16px #00000059}.fade-enter-active[data-v-2323ca85],.fade-leave-active[data-v-2323ca85]{transition:opacity .16s}.fade-enter-from[data-v-2323ca85],.fade-leave-to[data-v-2323ca85]{opacity:0}.activity-feed-nav-btn[data-v-2323ca85]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-2323ca85]:hover{opacity:1}.activity-feed-scroll[data-v-2323ca85] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-2323ca85]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}.activity-feed-empty[data-v-2323ca85]{align-items:center;gap:var(--kobo-space-sm);padding:var(--kobo-space-4xl) var(--kobo-space-xl);color:var(--kobo-text-3);text-align:center;flex-direction:column;display:flex}.activity-feed-empty__title[data-v-2323ca85]{font-family:var(--kobo-font-sans);color:var(--kobo-text-2);font-size:14px;font-weight:500}.activity-feed-empty__hint[data-v-2323ca85]{font-family:var(--kobo-font-sans);color:var(--kobo-text-3);max-width:380px;font-size:13px}
@@ -0,0 +1 @@
1
+ import{B as e,C as t,wt as n}from"./reactivity.esm-bundler-DtSZKRQB.js";import{$ as r,A as i,H as a,Mt as o,Rt as s,U as c,V as l,W as u,Wt as d,Z as f,_n as p}from"./runtime-dom.esm-bundler-DgevF6KV.js";import{t as m}from"./QBtn-B7WFdfmy.js";import{_t as h,ht as g,r as _}from"./index-Dv1p-1sA.js";import{i as v}from"./layout-nWIyHeJf.js";import{n as y}from"./render-chat-markdown-B6sa44Xr.js";import{t as b}from"./QSpace-D3PNLwa4.js";import{t as x}from"./QChip-BzB_GF4Z.js";import{t as S}from"./QPage-RpDxx5ah.js";import{t as C}from"./DrawerToggleButton-CBdOJzVZ.js";var w={class:`row items-center q-mb-md`},T={class:`text-h6 q-ml-sm`},E={key:0,class:`text-kobo-3 text-center q-pa-lg`},D={key:1,class:`text-negative text-center q-pa-lg`},O={key:2,class:`text-kobo-3 text-center q-pa-lg`},k={key:3,class:`column q-gutter-md`},A={key:0,class:`text-caption text-kobo-3`},j={class:`row items-center q-mb-sm`},M={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},N=[`innerHTML`],P=v(r({__name:`ChangelogPage`,setup(r){let v=_(),P=t([]),F=t(``),I=t(!1),L=t(null);function R(e){return y(e)}async function z(){I.value=!0,L.value=null;try{let e=await fetch(`/api/changelog`);if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();F.value=t.currentVersion??``,P.value=t.versions??[]}catch(e){L.value=e instanceof Error?e.message:String(e)}finally{I.value=!1}}return o(z),(t,r)=>(s(),a(S,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:p(()=>[l(`div`,w,[f(C,{class:`q-mr-xs`}),f(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:r[0]||=t=>e(v).back()}),l(`div`,T,n(t.$t(`changelog.title`)),1),f(b),f(m,{flat:``,dense:``,icon:`refresh`,loading:I.value,label:t.$t(`common.refresh`),onClick:z},null,8,[`loading`,`label`])]),I.value&&P.value.length===0?(s(),u(`div`,E,n(t.$t(`common.loading`)),1)):L.value?(s(),u(`div`,D,n(L.value),1)):P.value.length===0?(s(),u(`div`,O,n(t.$t(`changelog.empty`)),1)):(s(),u(`div`,k,[F.value?(s(),u(`div`,A,n(t.$t(`changelog.currentVersion`,{version:F.value})),1)):c(``,!0),(s(!0),u(i,null,d(P.value,e=>(s(),a(g,{key:e.version,dark:``,flat:``,bordered:``},{default:p(()=>[f(h,null,{default:p(()=>[l(`div`,j,[l(`div`,M,` v`+n(e.version),1),e.version===F.value?(s(),a(x,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:t.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),l(`div`,{class:`changelog-notes`,innerHTML:R(e.notes)},null,8,N)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-303054e6`]]);export{P as default};
@@ -0,0 +1 @@
1
+ .changelog-notes[data-v-303054e6]{color:var(--kobo-text-2);font-size:13px;line-height:1.55}.changelog-notes[data-v-303054e6] h3{color:var(--kobo-text);margin:.8em 0 .3em;font-size:14px;font-weight:600}.changelog-notes[data-v-303054e6] ul{margin:0;padding-left:1.2em}.changelog-notes[data-v-303054e6] li{margin:.2em 0}.changelog-notes[data-v-303054e6] code{background:#00000040;border-radius:3px;padding:.1em .3em;font-size:12px}.changelog-notes[data-v-303054e6] a{color:var(--kobo-text-2)}
@@ -0,0 +1 @@
1
+ import{Q as e,R as t}from"./QBtn-B7WFdfmy.js";import{v as n,y as r}from"./use-id-DlouqdB8.js";var i={left:!0,right:!0,up:!0,down:!0,horizontal:!0,vertical:!0},a=Object.keys(i);i.all=!0;function o(e){let t={};for(let n of a)e[n]&&(t[n]=!0);return Object.keys(t).length===0?i:(t.horizontal?t.left=t.right=!0:t.left&&t.right&&(t.horizontal=!0),t.vertical?t.up=t.down=!0:t.up&&t.down&&(t.vertical=!0),t.horizontal&&t.vertical&&(t.all=!0),t)}var s=[`INPUT`,`TEXTAREA`];function c(e,t){return t.event===void 0&&e.target!==void 0&&!e.target.draggable&&typeof t.handler==`function`&&!s.includes(e.target.nodeName.toUpperCase())&&(e.qClonedBy===void 0||!e.qClonedBy.includes(t.uid))}function l(e){return e===!1?0:e===!0||e===void 0?1:Number.parseInt(e,10)||0}var u=e({name:`close-popup`,beforeMount(e,{value:i}){let a={depth:l(i),handler(t){a.depth!==0&&setTimeout(()=>{let i=r(e);i!==void 0&&n(i,t,a.depth)},0)},handlerKey(e){t(e,13)&&a.handler(e)}};e.__qclosepopup=a,e.addEventListener(`click`,a.handler),e.addEventListener(`keyup`,a.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=l(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{o as n,c as r,u as t};