codexui-android 0.1.91 → 0.1.92

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 (37) hide show
  1. package/README.md +10 -1
  2. package/dist/assets/{ReviewPane-gbhCGpfh.js → ReviewPane-D1nOf7-W.js} +1 -2
  3. package/dist/assets/{ReviewPane-CyugWwTM.css → ReviewPane-DuPX5OZA.css} +1 -1
  4. package/dist/assets/{SkillsHub-xXcF9J80.js → SkillsHub-BpqR2Yu5.js} +2 -3
  5. package/dist/assets/{SkillsHub-Bg1Le103.css → SkillsHub-CTnWejwn.css} +1 -1
  6. package/dist/assets/ThreadConversation-3WaRKicR.css +1 -0
  7. package/dist/assets/ThreadConversation-DgJc2aJG.js +39 -0
  8. package/dist/assets/ThreadTerminalPanel-CGTJQ1BI.css +32 -0
  9. package/dist/assets/ThreadTerminalPanel-Dy-oA46U.js +38 -0
  10. package/dist/assets/common-BeuopZEI.js +0 -1
  11. package/dist/assets/index-Dj8HigAf.css +1 -0
  12. package/dist/assets/index-HcEz2bUL.js +64 -0
  13. package/dist/assets/{index.esm-DtVW_dfU.js → index.esm-Bi-9KxvS.js} +2 -3
  14. package/dist/assets/{index.esm-mbv_PYjX.js → index.esm-DECIu6Fp.js} +1 -2
  15. package/dist/assets/{index.esm-BilMXo9u.js → index.esm-DPq88-QA.js} +2 -3
  16. package/dist/index.html +2 -2
  17. package/dist-cli/index.js +2330 -1052
  18. package/dist-cli/index.js.map +1 -1
  19. package/package.json +17 -15
  20. package/scripts/dev.cjs +58 -0
  21. package/scripts/fix-pty-native-build.cjs +62 -0
  22. package/dist/assets/ReviewPane-gbhCGpfh.js.map +0 -1
  23. package/dist/assets/SkillsHub-xXcF9J80.js.map +0 -1
  24. package/dist/assets/ThreadConversation-BsN7bN3q.css +0 -1
  25. package/dist/assets/ThreadConversation-Dr0u8WbA.js +0 -40
  26. package/dist/assets/ThreadConversation-Dr0u8WbA.js.map +0 -1
  27. package/dist/assets/common-BeuopZEI.js.map +0 -1
  28. package/dist/assets/index-B81KnkV8.js +0 -92
  29. package/dist/assets/index-B81KnkV8.js.map +0 -1
  30. package/dist/assets/index-C_knKlTI.css +0 -1
  31. package/dist/assets/index.esm-BilMXo9u.js.map +0 -1
  32. package/dist/assets/index.esm-DtVW_dfU.js.map +0 -1
  33. package/dist/assets/index.esm-mbv_PYjX.js.map +0 -1
  34. package/dist-cli/chunk-PUR7OUAG.js +0 -127
  35. package/dist-cli/chunk-PUR7OUAG.js.map +0 -1
  36. package/dist-cli/instrument.js +0 -8
  37. package/dist-cli/instrument.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexui-android",
3
- "version": "0.1.91",
3
+ "version": "0.1.92",
4
4
  "description": "A lightweight web interface for Codex that runs on top of the Codex app-server, allowing remote access from any browser",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,28 +28,29 @@
28
28
  },
29
29
  "bin": {
30
30
  "codexapp": "dist-cli/index.js",
31
- "codexui": "dist-cli/index.js"
31
+ "codexui": "dist-cli/index.js",
32
+ "codexui-android": "dist-cli/index.js"
32
33
  },
33
34
  "files": [
34
35
  "dist/",
35
- "dist-cli/"
36
+ "dist-cli/",
37
+ "scripts/dev.cjs",
38
+ "scripts/fix-pty-native-build.cjs"
36
39
  ],
37
40
  "dependencies": {
38
- "@opentelemetry/semantic-conventions": "1.40.0",
39
- "@sentry/node": "10.48.0",
40
- "@sentry/opentelemetry": "10.48.0",
41
- "@sentry/profiling-node": "10.48.0",
42
- "@sentry/vue": "10.48.0",
41
+ "@xterm/addon-fit": "^0.11.0",
42
+ "@xterm/xterm": "^6.0.0",
43
43
  "commander": "^13.1.0",
44
44
  "express": "^5.1.0",
45
45
  "firebase": "^12.2.1",
46
46
  "highlight.js": "^11.11.1",
47
+ "node-pty": "^1.1.0",
48
+ "node-pty-prebuilt-multiarch": "0.10.1-pre.5",
47
49
  "qrcode-terminal": "^0.12.0",
48
50
  "ws": "^8.18.3"
49
51
  },
50
52
  "devDependencies": {
51
53
  "@playwright/test": "^1.59.1",
52
- "@sentry/vite-plugin": "^5.2.0",
53
54
  "@tailwindcss/vite": "^4.1.18",
54
55
  "@types/express": "^5.0.0",
55
56
  "@types/node": "^22.13.2",
@@ -61,20 +62,21 @@
61
62
  "tsup": "^8.4.0",
62
63
  "typescript": "^5.7.3",
63
64
  "vite": "^6.1.0",
65
+ "vitest": "^4.1.5",
64
66
  "vue": "^3.5.13",
65
67
  "vue-router": "^4.6.4",
66
68
  "vue-tsc": "^2.2.0"
67
69
  },
68
- "overrides": {
69
- "@opentelemetry/semantic-conventions": "1.40.0",
70
- "@sentry/opentelemetry": "10.48.0"
71
- },
72
70
  "scripts": {
73
- "dev": "pnpm install && CODEXUI_SANDBOX_MODE=danger-full-access CODEXUI_APPROVAL_POLICY=never vite",
71
+ "dev": "node scripts/dev.cjs",
74
72
  "dev:open": "pnpm run build:cli && sh -c 'pnpm run dev & VPID=$!; node dist-cli/index.js --open-project \"${1:-.}\"; wait $VPID' --",
75
73
  "build:frontend": "vue-tsc --noEmit && vite build",
76
74
  "build:cli": "tsup",
77
75
  "build": "pnpm run build:frontend && pnpm run build:cli",
78
- "preview": "vite preview"
76
+ "postinstall": "node scripts/fix-pty-native-build.cjs",
77
+ "test:unit": "vitest run",
78
+ "preview": "vite preview",
79
+ "profile:browser": "node scripts/profile-browser-runtime.cjs",
80
+ "profile:thread": "PROFILE_ROUTE='#/thread/019da7c0-4e12-7a91-837c-f7c11cc8ab6c' node scripts/profile-browser-runtime.cjs"
79
81
  }
80
82
  }
@@ -0,0 +1,58 @@
1
+ const { execFileSync, spawnSync } = require('node:child_process')
2
+ const { existsSync } = require('node:fs')
3
+ const { join } = require('node:path')
4
+
5
+ function isAndroidRuntime() {
6
+ if (process.platform === 'android') return true
7
+ if (process.env.TERMUX_VERSION) return true
8
+ if (process.env.PREFIX?.includes('/com.termux/')) return true
9
+ if (existsSync('/system/build.prop')) return true
10
+ try {
11
+ return execFileSync('uname', ['-r'], { encoding: 'utf8' }).toLowerCase().includes('android')
12
+ } catch {
13
+ return false
14
+ }
15
+ }
16
+
17
+ function run(command, args, options = {}) {
18
+ const result = spawnSync(command, args, {
19
+ stdio: 'inherit',
20
+ env: {
21
+ ...process.env,
22
+ CODEXUI_SANDBOX_MODE: process.env.CODEXUI_SANDBOX_MODE || 'danger-full-access',
23
+ CODEXUI_APPROVAL_POLICY: process.env.CODEXUI_APPROVAL_POLICY || 'never',
24
+ },
25
+ ...options,
26
+ })
27
+ if (result.error) {
28
+ throw result.error
29
+ }
30
+ process.exit(result.status ?? 1)
31
+ }
32
+
33
+ const passthroughArgs = process.argv.slice(2)
34
+
35
+ if (isAndroidRuntime()) {
36
+ const cliPath = join(process.cwd(), 'dist-cli', 'index.js')
37
+ if (!existsSync(cliPath)) {
38
+ run('pnpm', ['run', 'build:cli'])
39
+ }
40
+ run('node', [
41
+ cliPath,
42
+ '--no-open',
43
+ '--no-tunnel',
44
+ '--no-login',
45
+ '--no-password',
46
+ ...passthroughArgs,
47
+ ])
48
+ }
49
+
50
+ const install = spawnSync('pnpm', ['install'], { stdio: 'inherit', env: process.env })
51
+ if (install.error) {
52
+ throw install.error
53
+ }
54
+ if (install.status !== 0) {
55
+ process.exit(install.status ?? 1)
56
+ }
57
+
58
+ run(join(process.cwd(), 'node_modules', '.bin', process.platform === 'win32' ? 'vite.cmd' : 'vite'), passthroughArgs)
@@ -0,0 +1,62 @@
1
+ const { existsSync, lstatSync, readFileSync, realpathSync, rmSync, writeFileSync } = require('node:fs')
2
+ const { dirname, join } = require('node:path')
3
+ const { spawnSync } = require('node:child_process')
4
+
5
+ const PTY_PACKAGES = [
6
+ 'node-pty-prebuilt-multiarch',
7
+ 'node-pty',
8
+ ]
9
+
10
+ function packageRoot(name) {
11
+ try {
12
+ return dirname(require.resolve(`${name}/package.json`))
13
+ } catch {
14
+ return null
15
+ }
16
+ }
17
+
18
+ function isBrokenSymlink(path) {
19
+ try {
20
+ return lstatSync(path).isSymbolicLink() && !existsSync(realpathSync(path))
21
+ } catch {
22
+ try {
23
+ return lstatSync(path).isSymbolicLink() && !existsSync(path)
24
+ } catch {
25
+ return false
26
+ }
27
+ }
28
+ }
29
+
30
+ function patchMakefile(makefile) {
31
+ const source = readFileSync(makefile, 'utf8')
32
+ const patched = source.replace(
33
+ /^cmd_copy = ln -f "\$<" "\$@" 2>\/dev\/null \|\| \(rm -rf "\$@" && cp -af "\$<" "\$@"\)$/m,
34
+ 'cmd_copy = rm -rf "$@" && cp -af "$<" "$@"',
35
+ )
36
+ if (patched !== source) {
37
+ writeFileSync(makefile, patched)
38
+ }
39
+ }
40
+
41
+ for (const name of PTY_PACKAGES) {
42
+ const root = packageRoot(name)
43
+ if (!root) continue
44
+
45
+ const buildDir = join(root, 'build')
46
+ const makefile = join(buildDir, 'Makefile')
47
+ const binary = join(buildDir, 'Release', 'pty.node')
48
+ if (!existsSync(makefile) || !isBrokenSymlink(binary)) continue
49
+
50
+ try {
51
+ patchMakefile(makefile)
52
+ rmSync(binary, { force: true })
53
+ const result = spawnSync('make', ['BUILDTYPE=Release', '-C', buildDir], {
54
+ stdio: 'inherit',
55
+ })
56
+ if (result.status !== 0) {
57
+ console.warn(`[postinstall] Failed to repair ${name} native PTY build`)
58
+ }
59
+ } catch (error) {
60
+ console.warn(`[postinstall] Failed to repair ${name} native PTY build: ${error.message}`)
61
+ }
62
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"ReviewPane-gbhCGpfh.js","sources":["../../src/components/content/ReviewPane.vue"],"sourcesContent":["<template>\n <section class=\"review-pane\" :class=\"{ 'is-mobile': isMobile }\" @click.stop>\n <header class=\"review-pane-header\">\n <div class=\"review-pane-heading\">\n <p class=\"review-pane-eyebrow\">Review</p>\n <p class=\"review-pane-title\">{{ headerTitle }}</p>\n </div>\n <div class=\"review-pane-header-actions\">\n <button\n v-if=\"isMobile && activeTab === 'changes' && snapshot?.files.length\"\n type=\"button\"\n class=\"review-pane-mobile-files-button\"\n @click=\"isFileSheetOpen = true\"\n >\n Files\n </button>\n <button type=\"button\" class=\"review-pane-close\" aria-label=\"Close review pane\" @click=\"$emit('close')\">\n <IconTablerX class=\"icon-svg\" />\n </button>\n </div>\n </header>\n\n <div class=\"review-pane-toolbar\">\n <div class=\"review-pane-toolbar-tabs\">\n <div class=\"review-pane-segmented review-pane-segmented-primary\">\n <button\n v-for=\"tab in reviewTabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-tab\"\n :data-active=\"activeTab === tab.value\"\n @click=\"activeTab = tab.value\"\n >\n {{ tab.label }}\n </button>\n </div>\n </div>\n\n <div class=\"review-pane-toolbar-controls\">\n <div class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Compare</span>\n <div class=\"review-pane-segmented\">\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-scope\"\n :data-active=\"activeScope === 'workspace'\"\n @click=\"activeScope = 'workspace'\"\n >\n Workspace\n </button>\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-scope\"\n :data-active=\"activeScope === 'baseBranch'\"\n :disabled=\"!snapshot?.baseBranch\"\n @click=\"activeScope = 'baseBranch'\"\n >\n Base branch\n </button>\n </div>\n </div>\n\n <div v-if=\"activeScope === 'baseBranch' && snapshot?.baseBranchOptions.length\" class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Branch</span>\n <label class=\"review-pane-branch-select-wrap\">\n <select\n v-model=\"selectedBaseBranch\"\n class=\"review-pane-branch-select\"\n >\n <option\n v-for=\"branch in snapshot.baseBranchOptions\"\n :key=\"branch\"\n :value=\"branch\"\n >\n {{ branch }}\n </option>\n </select>\n </label>\n </div>\n\n <div v-if=\"activeScope === 'workspace'\" class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Changes</span>\n <div class=\"review-pane-segmented\">\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-view\"\n :data-active=\"workspaceView === 'unstaged'\"\n @click=\"workspaceView = 'unstaged'\"\n >\n Unstaged\n </button>\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-view\"\n :data-active=\"workspaceView === 'staged'\"\n @click=\"workspaceView = 'staged'\"\n >\n Staged\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"review-pane-toolbar-actions\">\n <button\n type=\"button\"\n class=\"review-pane-run\"\n :disabled=\"!canRunReview || isRunningReview\"\n @click=\"runReview\"\n >\n {{ isRunningReview ? 'Reviewing…' : 'Run review' }}\n </button>\n <button type=\"button\" class=\"review-pane-refresh\" :disabled=\"isLoadingSnapshot\" @click=\"reloadAll\">\n Refresh\n </button>\n </div>\n </div>\n\n <div v-if=\"reviewBannerText\" class=\"review-pane-banner\" :class=\"{ 'is-error': reviewBannerIsError }\">\n {{ reviewBannerText }}\n </div>\n\n <div v-if=\"snapshot\" class=\"review-pane-meta\">\n <span>{{ snapshot.summary.fileCount }} files</span>\n <span class=\"review-pane-summary-pill review-pane-summary-pill-add\">+{{ snapshot.summary.addedLineCount }}</span>\n <span class=\"review-pane-summary-pill review-pane-summary-pill-remove\">-{{ snapshot.summary.removedLineCount }}</span>\n <span v-if=\"snapshot.headBranch\">{{ snapshot.headBranch }}</span>\n <span v-if=\"activeScope === 'baseBranch' && snapshot.baseBranch\">vs {{ snapshot.baseBranch }}</span>\n </div>\n\n <div v-if=\"activeTab === 'changes'\" class=\"review-pane-content\">\n <template v-if=\"!snapshot\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">Loading review state</p>\n </div>\n </template>\n\n <template v-else-if=\"!snapshot.isGitRepo\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">This folder is not a Git repository</p>\n <p class=\"review-pane-empty-text\">Initialize Git to review local changes and run Codex review.</p>\n <button type=\"button\" class=\"review-pane-primary-cta\" :disabled=\"isInitializingGit\" @click=\"initializeGit\">\n {{ isInitializingGit ? 'Initializing…' : 'Initialize Git' }}\n </button>\n </div>\n </template>\n\n <template v-else-if=\"activeScope === 'baseBranch' && !snapshot.baseBranch\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">Base branch unavailable</p>\n <p class=\"review-pane-empty-text\">Could not resolve `origin/HEAD`, `main`, or `master` for this repository.</p>\n </div>\n </template>\n\n <template v-else>\n <div v-if=\"showBulkActions\" class=\"review-pane-bulk-actions\">\n <button\n v-for=\"action in bulkActions\"\n :key=\"action.value\"\n type=\"button\"\n class=\"review-pane-bulk-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyBulkAction(action.value)\"\n >\n {{ action.label }}\n </button>\n </div>\n\n <div v-if=\"!snapshot.files.length\" class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">No changes in this scope</p>\n <p class=\"review-pane-empty-text\">\n {{ activeScope === 'workspace' ? 'Your current workspace is clean.' : 'No merge diff found against the base branch.' }}\n </p>\n </div>\n\n <div v-else class=\"review-pane-main\" :style=\"reviewMainStyle\">\n <aside v-if=\"!isMobile\" class=\"review-pane-file-list\">\n <template v-for=\"node in visibleFileTreeNodes\" :key=\"node.treeKey\">\n <button\n v-if=\"node.kind === 'folder'\"\n type=\"button\"\n class=\"review-pane-tree-folder\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-expanded=\"isFolderExpanded(node.id)\"\n @click=\"toggleFolder(node.id)\"\n >\n <span class=\"review-pane-tree-caret\" :data-expanded=\"isFolderExpanded(node.id)\"></span>\n <span class=\"review-pane-tree-folder-name\">{{ node.name }}</span>\n <span class=\"review-pane-tree-folder-count\">{{ node.fileCount }}</span>\n </button>\n\n <button\n v-else\n type=\"button\"\n class=\"review-pane-file review-pane-tree-file\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-active=\"selectedFile?.id === node.file.id\"\n :title=\"node.file.path\"\n @click=\"selectFile(node.file.id)\"\n >\n <span class=\"review-pane-file-meta-row\">\n <span class=\"review-pane-file-main\">\n <span class=\"review-pane-file-op\" :data-operation=\"node.file.operation\">{{ formatOperation(node.file.operation) }}</span>\n <span class=\"review-pane-file-path\">\n {{ node.name }}\n <template v-if=\"node.file.previousPath\"> ← {{ fileBaseName(node.file.previousPath) }}</template>\n </span>\n </span>\n <span class=\"review-pane-file-delta\">\n <span class=\"review-pane-delta-add\">+{{ node.file.addedLineCount }}</span>\n <span class=\"review-pane-delta-separator\">/</span>\n <span class=\"review-pane-delta-remove\">-{{ node.file.removedLineCount }}</span>\n </span>\n </span>\n </button>\n </template>\n </aside>\n\n <div\n v-if=\"!isMobile\"\n class=\"review-pane-resizer\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize file list\"\n @pointerdown=\"onResizerPointerDown\"\n ></div>\n\n <section class=\"review-pane-diff\">\n <template v-if=\"selectedFile\">\n <div class=\"review-pane-file-header\">\n <div class=\"review-pane-file-header-main\">\n <p class=\"review-pane-file-title\">\n {{ selectedFile.path }}\n <template v-if=\"selectedFile.previousPath\"> ← {{ selectedFile.previousPath }}</template>\n </p>\n <p class=\"review-pane-file-subtitle\">\n {{ formatOperation(selectedFile.operation) }} · +{{ selectedFile.addedLineCount }} / -{{ selectedFile.removedLineCount }}\n </p>\n </div>\n <div v-if=\"showRowActions\" class=\"review-pane-row-actions\">\n <button\n v-for=\"action in fileActions\"\n :key=\"`${selectedFile.id}:${action.value}`\"\n type=\"button\"\n class=\"review-pane-row-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyFileAction(action.value, selectedFile)\"\n >\n {{ action.label }}\n </button>\n </div>\n </div>\n\n <div v-if=\"selectedFile.hunks.length === 0\" class=\"review-pane-raw-diff\">\n <pre>{{ selectedFile.diff || 'No unified diff available.' }}</pre>\n </div>\n\n <div v-else class=\"review-pane-hunks\">\n <article\n v-for=\"hunk in selectedFile.hunks\"\n :key=\"hunk.id\"\n :ref=\"(element) => bindHunkRef(hunk.id, element)\"\n class=\"review-pane-hunk\"\n :data-active=\"selectedHunkId === hunk.id\"\n @click=\"selectedHunkId = hunk.id\"\n >\n <div class=\"review-pane-hunk-header\">\n <div>\n <p class=\"review-pane-hunk-title\">{{ hunk.header }}</p>\n <p class=\"review-pane-hunk-meta\">+{{ hunk.addedLineCount }} / -{{ hunk.removedLineCount }}</p>\n </div>\n <div v-if=\"showRowActions\" class=\"review-pane-row-actions\">\n <button\n v-for=\"action in hunkActions\"\n :key=\"`${hunk.id}:${action.value}`\"\n type=\"button\"\n class=\"review-pane-row-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyHunkAction(action.value, hunk)\"\n >\n {{ action.label }}\n </button>\n </div>\n </div>\n\n <div class=\"review-pane-lines\">\n <div\n v-for=\"line in hunk.lines\"\n :key=\"line.key\"\n class=\"review-pane-line\"\n :data-kind=\"line.kind\"\n >\n <span class=\"review-pane-line-number\">{{ line.oldLine ?? '' }}</span>\n <span class=\"review-pane-line-number\">{{ line.newLine ?? '' }}</span>\n <span class=\"review-pane-line-marker\">{{ lineMarker(line.kind) }}</span>\n <code class=\"review-pane-line-code\">{{ line.text || ' ' }}</code>\n </div>\n </div>\n </article>\n </div>\n </template>\n </section>\n </div>\n </template>\n </div>\n\n <div v-else class=\"review-pane-findings\">\n <div v-if=\"currentReviewResult?.summary\" class=\"review-pane-summary-card\">\n <p class=\"review-pane-summary-title\">Summary</p>\n <pre class=\"review-pane-summary-text\">{{ currentReviewResult.summary }}</pre>\n </div>\n\n <div v-if=\"currentReviewResult?.findings.length\" class=\"review-pane-findings-list\">\n <button\n v-for=\"finding in currentReviewResult.findings\"\n :key=\"finding.id\"\n type=\"button\"\n class=\"review-pane-finding\"\n @click=\"openFinding(finding)\"\n >\n <span class=\"review-pane-finding-title\">{{ finding.title }}</span>\n <span v-if=\"finding.absolutePath\" class=\"review-pane-finding-location\">\n {{ formatFindingLocation(finding) }}\n </span>\n <span v-if=\"finding.body\" class=\"review-pane-finding-body\">{{ finding.body }}</span>\n </button>\n </div>\n\n <div v-else class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">No structured findings yet</p>\n <p class=\"review-pane-empty-text\">\n {{ currentReviewResult?.summary ? 'The latest review only returned summary text.' : 'Run review to populate this pane.' }}\n </p>\n </div>\n </div>\n\n <Transition name=\"review-pane-sheet\">\n <div\n v-if=\"isMobile && isFileSheetOpen && snapshot?.files.length\"\n class=\"review-pane-sheet-backdrop\"\n @click=\"isFileSheetOpen = false\"\n >\n <div class=\"review-pane-sheet\" @click.stop>\n <div class=\"review-pane-sheet-handle\" aria-hidden=\"true\"></div>\n <div class=\"review-pane-sheet-header\">\n <p class=\"review-pane-sheet-title\">Changed files</p>\n <p class=\"review-pane-sheet-count\">{{ snapshot.files.length }}</p>\n </div>\n <div class=\"review-pane-sheet-list\">\n <template v-for=\"node in visibleFileTreeNodes\" :key=\"`sheet:${node.treeKey}`\">\n <button\n v-if=\"node.kind === 'folder'\"\n type=\"button\"\n class=\"review-pane-tree-folder review-pane-tree-folder-sheet\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-expanded=\"isFolderExpanded(node.id)\"\n @click=\"toggleFolder(node.id)\"\n >\n <span class=\"review-pane-tree-caret\" :data-expanded=\"isFolderExpanded(node.id)\"></span>\n <span class=\"review-pane-tree-folder-name\">{{ node.name }}</span>\n <span class=\"review-pane-tree-folder-count\">{{ node.fileCount }}</span>\n </button>\n\n <button\n v-else\n type=\"button\"\n class=\"review-pane-file review-pane-tree-file\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-active=\"selectedFile?.id === node.file.id\"\n :title=\"node.file.path\"\n @click=\"selectFile(node.file.id)\"\n >\n <span class=\"review-pane-file-meta-row\">\n <span class=\"review-pane-file-main\">\n <span class=\"review-pane-file-op\" :data-operation=\"node.file.operation\">{{ formatOperation(node.file.operation) }}</span>\n <span class=\"review-pane-file-path\">\n {{ node.name }}\n <template v-if=\"node.file.previousPath\"> ← {{ fileBaseName(node.file.previousPath) }}</template>\n </span>\n </span>\n <span class=\"review-pane-file-delta\">\n <span class=\"review-pane-delta-add\">+{{ node.file.addedLineCount }}</span>\n <span class=\"review-pane-delta-separator\">/</span>\n <span class=\"review-pane-delta-remove\">-{{ node.file.removedLineCount }}</span>\n </span>\n </span>\n </button>\n </template>\n </div>\n </div>\n </div>\n </Transition>\n </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport {\n applyReviewAction,\n getReviewSnapshot,\n getThreadReviewResult,\n initializeReviewGit,\n startThreadReview,\n subscribeCodexNotifications,\n type RpcNotification,\n} from '../../api/codexGateway'\nimport { useMobile } from '../../composables/useMobile'\nimport type {\n UiReviewAction,\n UiReviewFinding,\n UiReviewResult,\n UiReviewScope,\n UiReviewSnapshot,\n UiReviewTab,\n UiReviewWorkspaceView,\n UiReviewFile,\n UiReviewHunk,\n} from '../../types/codex'\nimport IconTablerX from '../icons/IconTablerX.vue'\n\nconst props = defineProps<{\n threadId: string\n cwd: string\n isThreadInProgress: boolean\n}>()\n\ndefineEmits<{\n close: []\n}>()\n\nconst { isMobile } = useMobile()\n\nconst activeTab = ref<UiReviewTab>('changes')\nconst activeScope = ref<UiReviewScope>('workspace')\nconst workspaceView = ref<UiReviewWorkspaceView>('unstaged')\nconst snapshot = ref<UiReviewSnapshot | null>(null)\nconst selectedBaseBranch = ref('')\nconst isSyncingBaseBranch = ref(false)\nconst selectedFileId = ref('')\nconst selectedHunkId = ref('')\nconst isFileSheetOpen = ref(false)\nconst isLoadingSnapshot = ref(false)\nconst isApplyingAction = ref(false)\nconst isRunningReview = ref(false)\nconst isInitializingGit = ref(false)\nconst snapshotError = ref('')\nconst reviewError = ref('')\nconst reviewStatusLabel = ref('')\nconst reviewResultsByKey = ref<Record<string, UiReviewResult | null>>({})\nconst pendingReviewKey = ref('')\nconst hunkRefs = new Map<string, HTMLElement>()\nlet stopNotifications: (() => void) | null = null\nlet stopResizeTracking: (() => void) | null = null\n\nconst reviewTabs = [\n { value: 'changes' as const, label: 'Changes' },\n { value: 'findings' as const, label: 'Findings' },\n]\n\ntype ReviewTreeFolderNode = {\n kind: 'folder'\n treeKey: string\n id: string\n name: string\n depth: number\n fileCount: number\n}\n\ntype ReviewTreeFileNode = {\n kind: 'file'\n treeKey: string\n id: string\n name: string\n depth: number\n file: UiReviewFile\n}\n\ntype ReviewTreeNode = ReviewTreeFolderNode | ReviewTreeFileNode\n\ntype MutableReviewTreeFile = {\n file: UiReviewFile\n name: string\n depth: number\n}\n\ntype MutableReviewTreeFolder = {\n id: string\n name: string\n depth: number\n folders: Map<string, MutableReviewTreeFolder>\n files: MutableReviewTreeFile[]\n}\n\nconst reviewKey = computed(() => `${activeScope.value}:${workspaceView.value}`)\nconst currentReviewResult = computed(() => reviewResultsByKey.value[reviewKey.value] ?? null)\nconst selectedFile = computed(() => snapshot.value?.files.find((file) => file.id === selectedFileId.value) ?? snapshot.value?.files[0] ?? null)\nconst folderExpansionState = ref<Record<string, boolean>>({})\n\nconst headerTitle = computed(() => {\n if (!snapshot.value?.isGitRepo) return 'Repository review'\n if (activeScope.value === 'workspace') {\n return workspaceView.value === 'staged' ? 'Staged changes' : 'Workspace changes'\n }\n return snapshot.value?.baseBranch ? `Against ${snapshot.value.baseBranch}` : 'Base branch'\n})\n\nconst canRunReview = computed(() => (\n props.threadId.trim().length > 0\n && props.cwd.trim().length > 0\n && snapshot.value?.isGitRepo === true\n && !props.isThreadInProgress\n && !(activeScope.value === 'baseBranch' && !snapshot.value?.baseBranch)\n))\n\nconst showBulkActions = computed(() => (\n activeScope.value === 'workspace'\n && snapshot.value?.isGitRepo === true\n && snapshot.value.files.length > 0\n))\n\nconst showRowActions = computed(() => showBulkActions.value && !isApplyingAction.value)\n\nconst bulkActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage all' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage all' },\n { value: 'revert' as UiReviewAction, label: 'Revert all' },\n ]\n})\n\nconst fileActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage file' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage file' },\n { value: 'revert' as UiReviewAction, label: 'Revert file' },\n ]\n})\n\nconst hunkActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage hunk' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage hunk' },\n { value: 'revert' as UiReviewAction, label: 'Revert hunk' },\n ]\n})\n\nconst reviewBannerText = computed(() => (\n reviewError.value\n || snapshotError.value\n || reviewStatusLabel.value\n))\nconst reviewBannerIsError = computed(() => Boolean(reviewError.value || snapshotError.value))\nconst REVIEW_FILE_LIST_WIDTH_KEY = 'codex-web-local.review-pane-file-list-width.v1'\nconst MIN_FILE_LIST_WIDTH = 220\nconst MAX_FILE_LIST_WIDTH = 420\nconst DEFAULT_FILE_LIST_WIDTH = 288\nconst fileListWidth = ref(loadFileListWidth())\n\nconst reviewMainStyle = computed<Record<string, string>>(() => {\n const style: Record<string, string> = {}\n if (!isMobile.value) {\n style['--review-file-list-width'] = `${fileListWidth.value}px`\n }\n return style\n})\n\nconst fileTreeData = computed(() => buildVisibleFileTree(snapshot.value?.files ?? [], folderExpansionState.value))\nconst visibleFileTreeNodes = computed(() => fileTreeData.value.nodes)\nconst fileTreeFolderIdsByFileId = computed(() => fileTreeData.value.folderIdsByFileId)\n\nfunction clampFileListWidth(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_FILE_LIST_WIDTH\n return Math.min(MAX_FILE_LIST_WIDTH, Math.max(MIN_FILE_LIST_WIDTH, Math.round(value)))\n}\n\nfunction loadFileListWidth(): number {\n if (typeof window === 'undefined') return DEFAULT_FILE_LIST_WIDTH\n const raw = window.localStorage.getItem(REVIEW_FILE_LIST_WIDTH_KEY)\n const parsed = raw ? Number(raw) : Number.NaN\n return clampFileListWidth(parsed)\n}\n\nfunction persistFileListWidth(value: number): void {\n if (typeof window === 'undefined') return\n window.localStorage.setItem(REVIEW_FILE_LIST_WIDTH_KEY, String(clampFileListWidth(value)))\n}\n\nfunction onResizerPointerDown(event: PointerEvent): void {\n if (isMobile.value) return\n event.preventDefault()\n stopResizeTracking?.()\n const startX = event.clientX\n const startWidth = fileListWidth.value\n\n const handleMove = (moveEvent: PointerEvent) => {\n fileListWidth.value = clampFileListWidth(startWidth + (moveEvent.clientX - startX))\n }\n\n const cleanup = () => {\n window.removeEventListener('pointermove', handleMove)\n window.removeEventListener('pointerup', handleUp)\n stopResizeTracking = null\n }\n\n const handleUp = () => {\n cleanup()\n persistFileListWidth(fileListWidth.value)\n }\n\n window.addEventListener('pointermove', handleMove)\n window.addEventListener('pointerup', handleUp)\n stopResizeTracking = cleanup\n}\n\nfunction lineMarker(kind: string): string {\n if (kind === 'add') return '+'\n if (kind === 'remove') return '-'\n if (kind === 'hunk') return '@@'\n return ' '\n}\n\nfunction fileBaseName(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments[segments.length - 1] ?? path\n}\n\nfunction sortTreeEntries(left: string, right: string): number {\n return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' })\n}\n\nfunction buildVisibleFileTree(\n files: UiReviewFile[],\n expansionState: Record<string, boolean>,\n): {\n nodes: ReviewTreeNode[]\n folderIdsByFileId: Record<string, string[]>\n} {\n const root: MutableReviewTreeFolder = {\n id: '',\n name: '',\n depth: -1,\n folders: new Map(),\n files: [],\n }\n const folderIdsByFileId: Record<string, string[]> = {}\n\n for (const file of files) {\n const segments = file.path.split('/').filter(Boolean)\n const fileName = segments.pop() ?? file.path\n let currentFolder = root\n let folderPath = ''\n const parentFolderIds: string[] = []\n\n for (const [index, segment] of segments.entries()) {\n folderPath = folderPath ? `${folderPath}/${segment}` : segment\n let nextFolder = currentFolder.folders.get(segment)\n if (!nextFolder) {\n nextFolder = {\n id: folderPath,\n name: segment,\n depth: index,\n folders: new Map(),\n files: [],\n }\n currentFolder.folders.set(segment, nextFolder)\n }\n currentFolder = nextFolder\n parentFolderIds.push(nextFolder.id)\n }\n\n currentFolder.files.push({\n file,\n name: fileName,\n depth: segments.length,\n })\n folderIdsByFileId[file.id] = parentFolderIds\n }\n\n const nodes: ReviewTreeNode[] = []\n const countFiles = (folder: MutableReviewTreeFolder): number => (\n folder.files.length + Array.from(folder.folders.values()).reduce((sum, child) => sum + countFiles(child), 0)\n )\n\n const visitFolder = (folder: MutableReviewTreeFolder) => {\n const childFolders = Array.from(folder.folders.values()).sort((left, right) => sortTreeEntries(left.name, right.name))\n const childFiles = [...folder.files].sort((left, right) => sortTreeEntries(left.name, right.name))\n\n for (const childFolder of childFolders) {\n nodes.push({\n kind: 'folder',\n treeKey: `folder:${childFolder.id}`,\n id: childFolder.id,\n name: childFolder.name,\n depth: childFolder.depth,\n fileCount: countFiles(childFolder),\n })\n if (expansionState[childFolder.id] !== false) {\n visitFolder(childFolder)\n }\n }\n\n for (const childFile of childFiles) {\n nodes.push({\n kind: 'file',\n treeKey: `file:${childFile.file.id}`,\n id: childFile.file.id,\n name: childFile.name,\n depth: childFile.depth,\n file: childFile.file,\n })\n }\n }\n\n visitFolder(root)\n return { nodes, folderIdsByFileId }\n}\n\nfunction isFolderExpanded(folderId: string): boolean {\n return folderExpansionState.value[folderId] !== false\n}\n\nfunction toggleFolder(folderId: string): void {\n folderExpansionState.value = {\n ...folderExpansionState.value,\n [folderId]: !isFolderExpanded(folderId),\n }\n}\n\nfunction expandFileAncestors(fileId: string): void {\n const folderIds = fileTreeFolderIdsByFileId.value[fileId] ?? []\n if (folderIds.length === 0) return\n const nextState = { ...folderExpansionState.value }\n let changed = false\n for (const folderId of folderIds) {\n if (nextState[folderId] === false) {\n nextState[folderId] = true\n changed = true\n }\n }\n if (changed) {\n folderExpansionState.value = nextState\n }\n}\n\nfunction treeIndentStyle(depth: number): Record<string, string> {\n const base = isMobile.value ? 8 : 10\n const step = isMobile.value ? 12 : 14\n return {\n paddingLeft: `${base + (depth * step)}px`,\n }\n}\n\nfunction formatOperation(operation: string): string {\n if (operation === 'add') return 'Added'\n if (operation === 'delete') return 'Deleted'\n if (operation === 'rename') return 'Renamed'\n return 'Modified'\n}\n\nfunction bindHunkRef(hunkId: string, element: unknown): void {\n if (!(element instanceof HTMLElement)) {\n hunkRefs.delete(hunkId)\n return\n }\n hunkRefs.set(hunkId, element)\n}\n\nfunction extractNotificationThreadId(notification: RpcNotification): string {\n const params = notification.params !== null && typeof notification.params === 'object' && !Array.isArray(notification.params)\n ? notification.params as Record<string, unknown>\n : null\n return typeof params?.threadId === 'string' ? params.threadId : ''\n}\n\nasync function loadSnapshot(): Promise<void> {\n if (!props.cwd.trim()) return\n isLoadingSnapshot.value = true\n snapshotError.value = ''\n try {\n const desiredBaseBranch = activeScope.value === 'baseBranch' ? selectedBaseBranch.value.trim() : ''\n const nextSnapshot = await getReviewSnapshot(\n props.cwd,\n activeScope.value,\n workspaceView.value,\n desiredBaseBranch || null,\n )\n if (nextSnapshot.baseBranchOptions.length > 0) {\n const normalizedBaseBranch = nextSnapshot.baseBranch ?? nextSnapshot.baseBranchOptions[0] ?? ''\n if (selectedBaseBranch.value !== normalizedBaseBranch) {\n isSyncingBaseBranch.value = true\n selectedBaseBranch.value = normalizedBaseBranch\n }\n } else {\n if (selectedBaseBranch.value !== '') {\n isSyncingBaseBranch.value = true\n selectedBaseBranch.value = ''\n }\n }\n snapshot.value = nextSnapshot\n const hasSelectedFile = nextSnapshot.files.some((file) => file.id === selectedFileId.value)\n if (!hasSelectedFile) {\n selectedFileId.value = nextSnapshot.files[0]?.id ?? ''\n selectedHunkId.value = nextSnapshot.files[0]?.hunks[0]?.id ?? ''\n }\n } catch (error) {\n snapshotError.value = error instanceof Error ? error.message : 'Failed to load review snapshot'\n } finally {\n isLoadingSnapshot.value = false\n }\n}\n\nasync function loadLatestReviewResult(): Promise<void> {\n if (!props.threadId.trim()) return\n try {\n const reviewState = await getThreadReviewResult(props.threadId)\n if (reviewState.result) {\n reviewResultsByKey.value = {\n ...reviewResultsByKey.value,\n [reviewKey.value]: reviewState.result,\n }\n }\n } catch {\n // Keep the pane usable even if thread history refresh fails.\n }\n}\n\nasync function reloadAll(): Promise<void> {\n await Promise.all([\n loadSnapshot(),\n loadLatestReviewResult(),\n ])\n}\n\nfunction selectFile(fileId: string): void {\n expandFileAncestors(fileId)\n selectedFileId.value = fileId\n const file = snapshot.value?.files.find((entry) => entry.id === fileId) ?? null\n selectedHunkId.value = file?.hunks[0]?.id ?? ''\n if (isMobile.value) {\n isFileSheetOpen.value = false\n }\n}\n\nasync function applyAction(action: UiReviewAction, level: 'all' | 'file' | 'hunk', patch = ''): Promise<void> {\n if (!snapshot.value) return\n isApplyingAction.value = true\n reviewError.value = ''\n try {\n const nextSnapshot = await applyReviewAction({\n cwd: props.cwd,\n scope: activeScope.value,\n workspaceView: workspaceView.value,\n action,\n level,\n patch,\n })\n snapshot.value = nextSnapshot\n const hasSelectedFile = nextSnapshot.files.some((file) => file.id === selectedFileId.value)\n if (!hasSelectedFile) {\n selectedFileId.value = nextSnapshot.files[0]?.id ?? ''\n selectedHunkId.value = nextSnapshot.files[0]?.hunks[0]?.id ?? ''\n }\n } catch (error) {\n reviewError.value = error instanceof Error ? error.message : 'Failed to apply review action'\n } finally {\n isApplyingAction.value = false\n }\n}\n\nasync function applyBulkAction(action: UiReviewAction): Promise<void> {\n await applyAction(action, 'all')\n}\n\nasync function applyFileAction(action: UiReviewAction, file: UiReviewFile): Promise<void> {\n await applyAction(action, 'file', file.diff)\n}\n\nasync function applyHunkAction(action: UiReviewAction, hunk: UiReviewHunk): Promise<void> {\n selectedHunkId.value = hunk.id\n await applyAction(action, 'hunk', hunk.patch)\n}\n\nasync function initializeGit(): Promise<void> {\n if (!props.cwd.trim()) return\n isInitializingGit.value = true\n reviewError.value = ''\n try {\n await initializeReviewGit(props.cwd)\n await loadSnapshot()\n } catch (error) {\n reviewError.value = error instanceof Error ? error.message : 'Failed to initialize Git'\n } finally {\n isInitializingGit.value = false\n }\n}\n\nasync function runReview(): Promise<void> {\n if (!canRunReview.value || isRunningReview.value) return\n reviewError.value = ''\n reviewStatusLabel.value = activeScope.value === 'workspace'\n ? 'Reviewing current changes'\n : `Reviewing against ${snapshot.value?.baseBranch ?? 'base branch'}`\n isRunningReview.value = true\n pendingReviewKey.value = reviewKey.value\n\n try {\n await startThreadReview(\n props.threadId,\n activeScope.value,\n workspaceView.value,\n selectedBaseBranch.value || (snapshot.value?.baseBranch ?? null),\n )\n } catch (error) {\n isRunningReview.value = false\n reviewStatusLabel.value = ''\n reviewError.value = error instanceof Error ? error.message : 'Failed to start review'\n }\n}\n\nfunction formatFindingLocation(finding: UiReviewFinding): string {\n if (!finding.absolutePath) return ''\n const lineSuffix = finding.startLine ? `:${finding.startLine}${finding.endLine && finding.endLine !== finding.startLine ? `-${finding.endLine}` : ''}` : ''\n return `${finding.absolutePath}${lineSuffix}`\n}\n\nfunction findMatchingHunk(file: UiReviewFile, finding: UiReviewFinding): UiReviewHunk | null {\n if (!finding.startLine) return file.hunks[0] ?? null\n for (const hunk of file.hunks) {\n if (hunk.newStart !== null) {\n const newEnd = hunk.newStart + Math.max(hunk.newLineCount, 1) - 1\n if (finding.startLine >= hunk.newStart && finding.startLine <= newEnd) {\n return hunk\n }\n }\n if (hunk.oldStart !== null) {\n const oldEnd = hunk.oldStart + Math.max(hunk.oldLineCount, 1) - 1\n if (finding.startLine >= hunk.oldStart && finding.startLine <= oldEnd) {\n return hunk\n }\n }\n }\n return file.hunks[0] ?? null\n}\n\nasync function scrollToHunk(hunkId: string): Promise<void> {\n await nextTick()\n const element = hunkRefs.get(hunkId)\n element?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n}\n\nasync function openFinding(finding: UiReviewFinding): Promise<void> {\n activeTab.value = 'changes'\n const file = snapshot.value?.files.find((entry) => (\n entry.absolutePath === finding.absolutePath\n || entry.previousAbsolutePath === finding.absolutePath\n )) ?? null\n if (!file) return\n\n expandFileAncestors(file.id)\n selectedFileId.value = file.id\n const matchedHunk = findMatchingHunk(file, finding)\n selectedHunkId.value = matchedHunk?.id ?? ''\n if (matchedHunk?.id) {\n await scrollToHunk(matchedHunk.id)\n }\n}\n\nfunction handleNotification(notification: RpcNotification): void {\n if (extractNotificationThreadId(notification) !== props.threadId) return\n const params = notification.params !== null && typeof notification.params === 'object' && !Array.isArray(notification.params)\n ? notification.params as Record<string, unknown>\n : null\n const item = params?.item !== null && typeof params?.item === 'object' && !Array.isArray(params.item)\n ? params.item as Record<string, unknown>\n : null\n const itemType = typeof item?.type === 'string' ? item.type : ''\n\n if (notification.method === 'item/started' && itemType === 'enteredReviewMode') {\n isRunningReview.value = true\n reviewStatusLabel.value = typeof item?.review === 'string' ? item.review : 'Review in progress'\n return\n }\n\n if (notification.method === 'item/completed' && itemType === 'exitedReviewMode') {\n const targetKey = pendingReviewKey.value || reviewKey.value\n isRunningReview.value = false\n reviewStatusLabel.value = ''\n void getThreadReviewResult(props.threadId)\n .then((reviewState) => {\n if (!reviewState.result) return\n reviewResultsByKey.value = {\n ...reviewResultsByKey.value,\n [targetKey]: reviewState.result,\n }\n activeTab.value = 'findings'\n })\n .catch((error) => {\n reviewError.value = error instanceof Error ? error.message : 'Failed to load review result'\n })\n .finally(() => {\n pendingReviewKey.value = ''\n })\n }\n}\n\nwatch(\n () => [props.threadId, props.cwd] as const,\n () => {\n selectedFileId.value = ''\n selectedHunkId.value = ''\n reviewResultsByKey.value = {}\n pendingReviewKey.value = ''\n reviewError.value = ''\n reviewStatusLabel.value = ''\n void reloadAll()\n },\n { immediate: true },\n)\n\nwatch(\n () => [activeScope.value, workspaceView.value] as const,\n () => {\n selectedFileId.value = ''\n selectedHunkId.value = ''\n reviewError.value = ''\n snapshotError.value = ''\n void loadSnapshot()\n },\n)\n\nwatch(selectedBaseBranch, (branch, previous) => {\n if (isSyncingBaseBranch.value) {\n isSyncingBaseBranch.value = false\n return\n }\n if (activeScope.value !== 'baseBranch') return\n if (!branch || branch === previous) return\n void loadSnapshot()\n})\n\nwatch(selectedFile, (file) => {\n if (!file) return\n expandFileAncestors(file.id)\n if (!file.hunks.some((hunk) => hunk.id === selectedHunkId.value)) {\n selectedHunkId.value = file.hunks[0]?.id ?? ''\n }\n})\n\nwatch(selectedHunkId, (hunkId) => {\n if (!hunkId) return\n void scrollToHunk(hunkId)\n})\n\nonMounted(() => {\n stopNotifications = subscribeCodexNotifications(handleNotification)\n})\n\nonBeforeUnmount(() => {\n if (stopNotifications) {\n stopNotifications()\n stopNotifications = null\n }\n stopResizeTracking?.()\n})\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.review-pane {\n @apply flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-white;\n}\n\n.review-pane.is-mobile {\n @apply fixed inset-0 z-40 rounded-none border-0;\n}\n\n.review-pane-header {\n @apply flex items-start justify-between gap-3 border-b border-zinc-200 px-3 py-2.5;\n}\n\n.review-pane-heading {\n @apply min-w-0;\n}\n\n.review-pane-eyebrow {\n @apply m-0 text-[11px] uppercase tracking-[0.12em] text-zinc-400;\n}\n\n.review-pane-title {\n @apply m-0 truncate text-sm font-medium text-zinc-900;\n}\n\n.review-pane-header-actions {\n @apply flex items-center gap-2;\n}\n\n.review-pane-close,\n.review-pane-mobile-files-button,\n.review-pane-refresh,\n.review-pane-run,\n.review-pane-bulk-button,\n.review-pane-row-button,\n.review-pane-primary-cta {\n @apply rounded-full border border-zinc-200 bg-white px-2.5 py-1.25 text-[11px] text-zinc-700 transition hover:bg-zinc-50 disabled:cursor-default disabled:opacity-50;\n}\n\n.review-pane-close {\n @apply flex h-7.5 w-7.5 items-center justify-center rounded-full p-0;\n}\n\n.review-pane-toolbar {\n @apply flex flex-col gap-2 border-b border-zinc-100 px-3 py-2.5;\n}\n\n.review-pane-toolbar-tabs {\n @apply min-w-0;\n}\n\n.review-pane-toolbar-controls {\n @apply flex flex-wrap items-center gap-2;\n}\n\n.review-pane-control-cluster {\n @apply flex min-w-0 items-center gap-1.5;\n}\n\n.review-pane-control-label {\n @apply shrink-0 text-[10px] font-medium uppercase tracking-[0.08em] text-zinc-400;\n}\n\n.review-pane-branch-select-wrap {\n @apply inline-flex min-w-[9rem] items-center rounded-full border border-zinc-200 bg-white px-2.5 py-1 shadow-sm;\n}\n\n.review-pane-branch-select {\n @apply w-full appearance-none bg-transparent text-[11px] font-medium text-zinc-700 outline-none;\n}\n\n.review-pane-segmented {\n @apply inline-flex min-w-0 items-center gap-1 rounded-full bg-zinc-100 p-1;\n}\n\n.review-pane-segmented-primary {\n @apply flex-1 bg-zinc-100/80;\n}\n\n.review-pane-segmented-button {\n @apply relative min-w-0 rounded-full border border-transparent px-2.5 py-1.25 text-[11px] font-medium text-zinc-500 transition-colors;\n}\n\n.review-pane-segmented-button::before {\n content: '';\n @apply mr-1.5 inline-block h-1.5 w-1.5 rounded-full bg-zinc-300 align-middle transition-colors;\n}\n\n.review-pane-segmented-button[data-active='true'] {\n @apply border-sky-200 bg-sky-600 text-white shadow-sm;\n}\n\n.review-pane-segmented-button[data-active='true']::before {\n @apply bg-white;\n}\n\n.review-pane-segmented-button:disabled {\n @apply opacity-45;\n}\n\n.review-pane-toolbar-actions {\n @apply flex shrink-0 items-center gap-1.5;\n}\n\n.review-pane-run {\n @apply border-emerald-600 bg-emerald-600 text-white hover:bg-emerald-700;\n}\n\n.review-pane-refresh {\n @apply border-amber-300 bg-amber-50 text-amber-900 hover:bg-amber-100;\n}\n\n.review-pane-banner {\n @apply mx-3 mt-2.5 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800;\n}\n\n.review-pane-banner.is-error {\n @apply border-rose-200 bg-rose-50 text-rose-700;\n}\n\n.review-pane-meta {\n @apply flex flex-wrap items-center gap-1.5 px-3 pt-2.5 text-[11px] text-zinc-500;\n}\n\n.review-pane-meta span {\n @apply rounded-full bg-zinc-100 px-2 py-1;\n}\n\n.review-pane-summary-pill.review-pane-summary-pill-add {\n @apply bg-emerald-100 text-emerald-700;\n}\n\n.review-pane-summary-pill.review-pane-summary-pill-remove {\n @apply bg-rose-100 text-rose-700;\n}\n\n.review-pane-content,\n.review-pane-findings {\n @apply min-h-0 flex-1 overflow-hidden;\n}\n\n.review-pane-bulk-actions {\n @apply flex flex-nowrap gap-1.5 overflow-x-auto border-b border-zinc-100 px-3 py-2.5;\n}\n\n.review-pane-main {\n @apply grid h-full min-h-0 grid-cols-[var(--review-file-list-width,18rem)_0.5rem_minmax(0,1fr)];\n}\n\n.review-pane-file-list {\n @apply hidden min-w-0 overflow-y-auto border-r border-zinc-100 bg-zinc-50/60 p-2 md:flex md:flex-col md:gap-1.5;\n}\n\n.review-pane-tree-folder {\n @apply flex w-full items-center gap-1 rounded-lg border border-transparent px-2 py-1.5 text-left text-[12px] font-medium text-zinc-600 transition hover:bg-white hover:text-zinc-900;\n}\n\n.review-pane-tree-folder[data-expanded='false'] {\n @apply text-zinc-500;\n}\n\n.review-pane-tree-folder-sheet {\n @apply rounded-md bg-zinc-50/80;\n}\n\n.review-pane-tree-caret {\n @apply relative h-3.5 w-3.5 shrink-0;\n}\n\n.review-pane-tree-caret::before {\n content: '';\n @apply absolute left-1 top-1 h-0 w-0 border-y-[4px] border-l-[5px] border-y-transparent border-l-current transition-transform;\n}\n\n.review-pane-tree-caret[data-expanded='true']::before {\n transform: rotate(90deg);\n transform-origin: 2px 4px;\n}\n\n.review-pane-tree-folder-name {\n @apply min-w-0 flex-1 truncate;\n}\n\n.review-pane-tree-folder-count {\n @apply shrink-0 rounded-full bg-zinc-200 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500;\n}\n\n.review-pane-resizer {\n @apply relative hidden cursor-col-resize bg-zinc-100 md:block;\n}\n\n.review-pane-resizer::before {\n content: '';\n @apply absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-zinc-300 transition-colors;\n}\n\n.review-pane-resizer:hover::before {\n @apply bg-sky-500;\n}\n\n.review-pane-file,\n.review-pane-finding {\n @apply flex w-full flex-col gap-0.75 rounded-xl border border-transparent px-2.5 py-2 text-left transition hover:border-zinc-200 hover:bg-white;\n}\n\n.review-pane-tree-file {\n @apply rounded-lg px-2 py-1.75;\n}\n\n.review-pane-file-meta-row {\n @apply flex items-start justify-between gap-2;\n}\n\n.review-pane-file-main {\n @apply flex min-w-0 items-center gap-1.5;\n}\n\n.review-pane-file[data-active='true'] {\n @apply border-zinc-300 bg-white shadow-sm;\n}\n\n.review-pane-file-op {\n @apply inline-flex w-fit rounded-full px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.08em];\n}\n\n.review-pane-file-op[data-operation='add'] {\n @apply bg-emerald-100 text-emerald-800;\n}\n\n.review-pane-file-op[data-operation='delete'] {\n @apply bg-rose-100 text-rose-700;\n}\n\n.review-pane-file-op[data-operation='rename'] {\n @apply bg-sky-100 text-sky-700;\n}\n\n.review-pane-file-op[data-operation='update'] {\n @apply bg-amber-100 text-amber-800;\n}\n\n.review-pane-file-path {\n @apply min-w-0 break-all text-sm text-zinc-800;\n}\n\n.review-pane-file-delta {\n @apply inline-flex shrink-0 items-center gap-1 whitespace-nowrap text-[11px];\n}\n\n.review-pane-delta-add {\n @apply text-emerald-600;\n}\n\n.review-pane-delta-remove {\n @apply text-rose-600;\n}\n\n.review-pane-delta-separator {\n @apply text-zinc-400;\n}\n\n.review-pane-diff {\n @apply min-h-0 overflow-y-auto px-3 py-3;\n}\n\n.review-pane-file-header,\n.review-pane-hunk {\n @apply rounded-2xl border border-zinc-200 bg-white;\n}\n\n.review-pane-file-header {\n @apply mb-3 flex flex-wrap items-start justify-between gap-2 px-3 py-2.5;\n}\n\n.review-pane-file-title {\n @apply m-0 break-all text-sm font-medium text-zinc-900;\n}\n\n.review-pane-file-subtitle,\n.review-pane-hunk-meta,\n.review-pane-finding-location {\n @apply m-0 text-[11px] text-zinc-500;\n}\n\n.review-pane-row-actions {\n @apply flex flex-wrap gap-1.5;\n}\n\n.review-pane-hunks {\n @apply flex flex-col gap-2.5;\n}\n\n.review-pane-hunk {\n @apply overflow-hidden;\n}\n\n.review-pane-hunk[data-active='true'] {\n @apply border-zinc-400 shadow-[0_0_0_1px_rgba(24,24,27,0.08)];\n}\n\n.review-pane-hunk-header {\n @apply flex flex-wrap items-start justify-between gap-2 border-b border-zinc-100 bg-zinc-50/70 px-3 py-2.5;\n}\n\n.review-pane-hunk-title {\n @apply m-0 font-mono text-xs text-zinc-800;\n}\n\n.review-pane-lines {\n @apply overflow-x-auto bg-zinc-950 px-0 py-0 font-mono text-xs text-zinc-100;\n}\n\n.review-pane-line {\n @apply grid min-w-max grid-cols-[3.5rem_3.5rem_1.5rem_minmax(0,1fr)] gap-0;\n}\n\n.review-pane-line-number {\n @apply px-2.5 py-1 text-right text-zinc-500;\n}\n\n.review-pane-line-marker {\n @apply px-2 py-1 text-center text-zinc-500;\n}\n\n.review-pane-line-code {\n @apply block px-2.5 py-1 whitespace-pre-wrap break-all;\n}\n\n.review-pane-line[data-kind='add'] {\n @apply bg-emerald-950/60 text-emerald-100;\n}\n\n.review-pane-line[data-kind='remove'] {\n @apply bg-rose-950/60 text-rose-100;\n}\n\n.review-pane-line[data-kind='add'] .review-pane-line-marker,\n.review-pane-line[data-kind='add'] .review-pane-line-code {\n @apply text-emerald-300;\n}\n\n.review-pane-line[data-kind='remove'] .review-pane-line-marker,\n.review-pane-line[data-kind='remove'] .review-pane-line-code {\n @apply text-rose-300;\n}\n\n.review-pane-line[data-kind='hunk'] {\n @apply bg-sky-950/70 text-sky-200;\n}\n\n.review-pane-line[data-kind='meta'] {\n @apply bg-zinc-900 text-zinc-400;\n}\n\n.review-pane-raw-diff {\n @apply overflow-x-auto rounded-2xl border border-zinc-200 bg-zinc-950 p-3 text-xs text-zinc-100;\n}\n\n.review-pane-raw-diff pre,\n.review-pane-summary-text {\n @apply m-0 whitespace-pre-wrap break-all font-mono;\n}\n\n.review-pane-summary-card {\n @apply mx-3 mt-3 rounded-2xl border border-zinc-200 bg-zinc-50 px-3 py-2.5;\n}\n\n.review-pane-summary-title {\n @apply m-0 mb-2 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-findings-list {\n @apply flex h-full flex-col gap-2.5 overflow-y-auto px-3 py-3;\n}\n\n.review-pane-finding {\n @apply border-zinc-200 bg-white hover:border-zinc-300;\n}\n\n.review-pane-finding-title {\n @apply text-sm font-medium text-zinc-900;\n}\n\n.review-pane-finding-body {\n @apply text-sm text-zinc-600 whitespace-pre-wrap;\n}\n\n.review-pane-empty {\n @apply flex h-full min-h-0 flex-col items-center justify-center px-6 text-center;\n}\n\n.review-pane-empty-title {\n @apply m-0 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-empty-text {\n @apply mt-2 max-w-sm text-sm text-zinc-500;\n}\n\n.review-pane-primary-cta {\n @apply mt-4 border-emerald-600 bg-emerald-600 text-white hover:bg-emerald-700;\n}\n\n.review-pane-sheet-backdrop {\n @apply fixed inset-0 z-50 bg-black/30;\n}\n\n.review-pane-sheet {\n @apply absolute inset-x-0 bottom-0 rounded-t-3xl bg-white px-4 pb-6 pt-3 shadow-2xl;\n}\n\n.review-pane-sheet-handle {\n @apply mx-auto mb-3 h-1.5 w-12 rounded-full bg-zinc-300;\n}\n\n.review-pane-sheet-header {\n @apply mb-3 flex items-center justify-between;\n}\n\n.review-pane-sheet-title {\n @apply m-0 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-sheet-count {\n @apply m-0 rounded-full bg-zinc-100 px-2 py-1 text-[11px] text-zinc-500;\n}\n\n.review-pane-sheet-list {\n @apply flex max-h-[60vh] flex-col gap-2 overflow-y-auto pb-3;\n}\n\n.review-pane-sheet-enter-active,\n.review-pane-sheet-leave-active {\n transition: opacity 160ms ease;\n}\n\n.review-pane-sheet-enter-active .review-pane-sheet,\n.review-pane-sheet-leave-active .review-pane-sheet {\n transition: transform 200ms ease;\n}\n\n.review-pane-sheet-enter-from,\n.review-pane-sheet-leave-to {\n opacity: 0;\n}\n\n.review-pane-sheet-enter-from .review-pane-sheet,\n.review-pane-sheet-leave-to .review-pane-sheet {\n transform: translateY(16px);\n}\n\n@media (max-width: 767px) {\n .review-pane-header {\n @apply px-3 py-2;\n }\n\n .review-pane-eyebrow {\n @apply text-[10px];\n }\n\n .review-pane-title {\n @apply text-xs leading-5;\n }\n\n .review-pane-header-actions {\n @apply gap-1.5;\n }\n\n .review-pane-close,\n .review-pane-mobile-files-button,\n .review-pane-refresh,\n .review-pane-run {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-close {\n @apply h-7 w-7;\n }\n\n .review-pane-toolbar {\n @apply gap-1.5 px-3 py-2;\n }\n\n .review-pane-toolbar-controls {\n @apply grid grid-cols-1 gap-1.5;\n }\n\n .review-pane-control-cluster {\n @apply gap-1;\n }\n\n .review-pane-control-label {\n @apply text-[9px];\n }\n\n .review-pane-branch-select-wrap {\n @apply min-w-0 flex-1 px-2 py-0.75;\n }\n\n .review-pane-branch-select {\n @apply text-[12px];\n }\n\n .review-pane-segmented {\n @apply w-full justify-between gap-1 p-0.75;\n }\n\n .review-pane-segmented-button {\n @apply flex-1 px-2 py-1 text-[12px];\n }\n\n .review-pane-toolbar-actions {\n @apply w-auto gap-1;\n }\n\n .review-pane-refresh,\n .review-pane-run {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-banner {\n @apply mx-3 mt-2 px-2.5 py-1.5 text-xs;\n }\n\n .review-pane-meta {\n @apply gap-1 px-3 pt-2;\n }\n\n .review-pane-meta span {\n @apply px-1.75 py-0.75 text-[11px];\n }\n\n .review-pane-bulk-actions {\n @apply gap-1 px-3 py-2;\n }\n\n .review-pane-bulk-button {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-main {\n @apply block;\n }\n\n .review-pane-resizer {\n @apply hidden;\n }\n\n .review-pane-diff {\n @apply px-2 py-2.5;\n }\n\n .review-pane-file-header,\n .review-pane-hunk-header {\n @apply px-2.5 py-2;\n }\n\n .review-pane-sheet {\n @apply px-3 pb-4 pt-2.5;\n }\n\n .review-pane-sheet-handle {\n @apply mb-2 h-1.25 w-11;\n }\n\n .review-pane-sheet-header {\n @apply mb-2;\n }\n\n .review-pane-sheet-title {\n @apply text-xs;\n }\n\n .review-pane-sheet-count {\n @apply px-1.5 py-0.75 text-[10px];\n }\n\n .review-pane-sheet-list {\n @apply gap-1.5 pb-2;\n }\n\n .review-pane-sheet-list .review-pane-tree-folder {\n @apply gap-1 rounded-md px-2 py-1 text-[12px];\n }\n\n .review-pane-sheet-list .review-pane-tree-folder-count {\n @apply px-1.25 py-0.25 text-[9px];\n }\n\n .review-pane-sheet-list .review-pane-file {\n @apply gap-0.5 rounded-lg px-2 py-1.5;\n }\n\n .review-pane-sheet-list .review-pane-file-meta-row {\n @apply gap-1.5;\n }\n\n .review-pane-sheet-list .review-pane-file-op {\n @apply px-1.5 py-0.25 text-[9px];\n }\n\n .review-pane-sheet-list .review-pane-file-main {\n @apply gap-1;\n }\n\n .review-pane-sheet-list .review-pane-file-path {\n @apply text-[13px] leading-5;\n }\n\n .review-pane-sheet-list .review-pane-file-delta {\n @apply text-[11px];\n }\n}\n\n@media (min-width: 768px) {\n .review-pane-toolbar {\n @apply flex-row items-center gap-2.5;\n }\n\n .review-pane-toolbar-tabs {\n @apply flex-1;\n }\n\n .review-pane-toolbar-controls {\n @apply min-w-0 flex-nowrap;\n }\n\n .review-pane-control-cluster {\n @apply shrink-0;\n }\n\n .review-pane-toolbar-actions {\n @apply ml-auto;\n }\n}\n</style>\n"],"names":["REVIEW_FILE_LIST_WIDTH_KEY","MIN_FILE_LIST_WIDTH","MAX_FILE_LIST_WIDTH","DEFAULT_FILE_LIST_WIDTH","props","__props","isMobile","useMobile","activeTab","ref","activeScope","workspaceView","snapshot","selectedBaseBranch","isSyncingBaseBranch","selectedFileId","selectedHunkId","isFileSheetOpen","isLoadingSnapshot","isApplyingAction","isRunningReview","isInitializingGit","snapshotError","reviewError","reviewStatusLabel","reviewResultsByKey","pendingReviewKey","hunkRefs","stopNotifications","stopResizeTracking","reviewTabs","reviewKey","computed","currentReviewResult","selectedFile","_a","file","_b","folderExpansionState","headerTitle","canRunReview","showBulkActions","showRowActions","bulkActions","fileActions","hunkActions","reviewBannerText","reviewBannerIsError","fileListWidth","loadFileListWidth","reviewMainStyle","style","fileTreeData","buildVisibleFileTree","visibleFileTreeNodes","fileTreeFolderIdsByFileId","clampFileListWidth","value","raw","parsed","persistFileListWidth","onResizerPointerDown","event","startX","startWidth","handleMove","moveEvent","cleanup","handleUp","lineMarker","kind","fileBaseName","path","segments","sortTreeEntries","left","right","files","expansionState","root","folderIdsByFileId","fileName","currentFolder","folderPath","parentFolderIds","index","segment","nextFolder","nodes","countFiles","folder","sum","child","visitFolder","childFolders","childFiles","childFolder","childFile","isFolderExpanded","folderId","toggleFolder","expandFileAncestors","fileId","folderIds","nextState","changed","treeIndentStyle","depth","base","step","formatOperation","operation","bindHunkRef","hunkId","element","extractNotificationThreadId","notification","params","loadSnapshot","desiredBaseBranch","nextSnapshot","getReviewSnapshot","normalizedBaseBranch","_c","error","loadLatestReviewResult","reviewState","getThreadReviewResult","reloadAll","selectFile","entry","applyAction","action","level","patch","applyReviewAction","applyBulkAction","applyFileAction","applyHunkAction","hunk","initializeGit","initializeReviewGit","runReview","startThreadReview","formatFindingLocation","finding","lineSuffix","findMatchingHunk","newEnd","oldEnd","scrollToHunk","nextTick","openFinding","matchedHunk","handleNotification","item","itemType","targetKey","watch","branch","previous","onMounted","subscribeCodexNotifications","onBeforeUnmount","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_1","_hoisted_2","_cache","_hoisted_3","_toDisplayString","_hoisted_4","$emit","_createVNode","IconTablerX","_hoisted_5","_hoisted_6","_hoisted_7","_Fragment","_renderList","tab","$event","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_openBlock","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_38","node","_normalizeStyle","_hoisted_41","_hoisted_42","_hoisted_44","_hoisted_45","_hoisted_46","_hoisted_47","_createTextVNode","_hoisted_48","_hoisted_49","_hoisted_50","_hoisted_51","_hoisted_52","_hoisted_53","_hoisted_54","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_61","_hoisted_62","_hoisted_63","_hoisted_64","_hoisted_65","_hoisted_66","line","_hoisted_68","_hoisted_69","_hoisted_70","_hoisted_71","_hoisted_36","_hoisted_37","_hoisted_31","_hoisted_32","_hoisted_30","_hoisted_72","_d","_hoisted_73","_hoisted_74","_e","_hoisted_75","_hoisted_77","_hoisted_78","_hoisted_79","_hoisted_80","_hoisted_81","_f","_Transition","_hoisted_82","_hoisted_83","_hoisted_84","_hoisted_87","_hoisted_88","_hoisted_90","_hoisted_91","_hoisted_92","_hoisted_93","_hoisted_94","_hoisted_95","_hoisted_96"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":["props","__props","skillDirPath","computed","p","onBrowse","dir","publishedLabel","ts","d","diff","onAvatarError","e","img","_createElementBlock","_normalizeClass","_cache","$event","$emit","_createElementVNode","_hoisted_1","_openBlock","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_createVNode","IconTablerFolder","_hoisted_10","_hoisted_11","emit","__emit","localEnabled","ref","localDescription","readmeContent","isLoadingReadme","effectiveEnabled","isActing","effectiveDescription","renderedReadme","raw","withoutFrontmatter","simpleMarkdown","md","fetchReadme","params","resp","data","watch","v","onInstall","onUninstall","onToggleEnabled","next","onBrowseFiles","_createBlock","_Teleport","_hoisted_2","IconTablerX","_hoisted_12","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","firebaseConfig","firebaseGithubAuthLoader","loadFirebaseGithubAuth","__vitePreload","useGithubSkillsSync","options","deviceLogin","syncActionStatus","syncActionError","syncActionInFlight","syncStatus","isPullInFlight","isPushInFlight","isStartupSyncInFlight","isSyncActionInFlight","loadSyncStatus","payload","startGithubLogin","startResp","startData","maxAttempts","waitMs","loggedIn","i","resolve","completeResp","completeData","startGithubFirebaseLogin","firebaseApp","firebaseAuth","getApp","getApps","initializeApp","getAuth","GithubAuthProvider","signInWithPopup","app","auth","provider","result","credential","token","error","message","pullSkillsSync","pushSkillsSync","startupSkillsSync","logoutGithub","SKILLS_HUB_CACHE_KEY","EMPTY_SKILL","searchRef","query","activeQuery","sortMode","browseSkills","installedSkills","totalCount","isLoading","isInstalledOpen","isDetailOpen","detailSkill","toast","actionSkillKey","isInstallActionInFlight","isUninstallActionInFlight","toastTimer","sortLabel","toastClass","_a","currentDetailSkillKey","isDetailInstalling","isDetailUninstalling","githubRepoUrl","owner","repo","filteredInstalled","q","s","showToast","text","type","toggleSort","fetchSkills","cacheKey","readCache","key","writeCache","byKey","clearCache","applySkillsPayload","inst","installedNames","local","normalizedQuery","cached","onSearchSubmit","openDetail","skill","handleInstall","installed","handleUninstall","handleToggleEnabled","enabled","onMounted","_unref","_hoisted_13","args","IconTablerChevronRight","_hoisted_20","_Fragment","_renderList","SkillCard","_hoisted_21","_hoisted_22","IconTablerSearch","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","SkillDetailModal"],"ignoreList":[],"sources":["../../src/components/content/SkillCard.vue","../../src/components/content/SkillDetailModal.vue","../../src/composables/useGithubSkillsSync.ts","../../src/components/content/SkillsHub.vue"],"sourcesContent":["<template>\n <button\n class=\"skill-card\"\n type=\"button\"\n :class=\"{ 'is-disabled': skill.installed && skill.enabled === false }\"\n @click=\"$emit('select', skill)\"\n >\n <div class=\"skill-card-top\">\n <img\n v-if=\"skill.avatarUrl\"\n class=\"skill-card-avatar\"\n :src=\"skill.avatarUrl\"\n :alt=\"skill.owner\"\n loading=\"lazy\"\n @error=\"onAvatarError\"\n />\n <div class=\"skill-card-avatar-fallback\" v-else>{{ skill.owner.charAt(0) }}</div>\n <div class=\"skill-card-info\">\n <div class=\"skill-card-header\">\n <span class=\"skill-card-name\">{{ skill.displayName || skill.name }}</span>\n <span v-if=\"skill.installed && skill.enabled === false\" class=\"skill-card-badge-disabled\">Disabled</span>\n <span v-else-if=\"skill.installed\" class=\"skill-card-badge\">Installed</span>\n </div>\n <span class=\"skill-card-owner\">{{ skill.owner }}</span>\n </div>\n <button\n v-if=\"skill.installed && skillDirPath\"\n class=\"skill-card-browse\"\n type=\"button\"\n title=\"Browse files\"\n @click.stop=\"onBrowse\"\n >\n <IconTablerFolder class=\"skill-card-browse-icon\" />\n </button>\n </div>\n <p v-if=\"skill.description\" class=\"skill-card-desc\">{{ skill.description }}</p>\n <span v-if=\"publishedLabel\" class=\"skill-card-date\">{{ publishedLabel }}</span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport IconTablerFolder from '../icons/IconTablerFolder.vue'\n\nconst props = defineProps<{\n skill: {\n name: string\n owner: string\n description: string\n displayName?: string\n publishedAt?: number\n avatarUrl?: string\n url: string\n installed: boolean\n path?: string\n enabled?: boolean\n }\n}>()\n\ndefineEmits<{ select: [skill: unknown] }>()\n\nconst skillDirPath = computed(() => {\n const p = props.skill.path\n if (!p) return ''\n return p.endsWith('/SKILL.md') ? p.slice(0, -'/SKILL.md'.length) : p\n})\n\nfunction onBrowse(): void {\n const dir = skillDirPath.value\n if (!dir) return\n window.open(`/codex-local-browse${encodeURI(dir)}`, '_blank', 'noopener,noreferrer')\n}\n\nconst publishedLabel = computed(() => {\n const ts = props.skill.publishedAt\n if (!ts) return ''\n const d = new Date(ts)\n const now = Date.now()\n const diff = now - ts\n if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`\n if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`\n if (diff < 2592000_000) return `${Math.floor(diff / 86400_000)}d ago`\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n})\n\nfunction onAvatarError(e: Event): void {\n const img = e.target as HTMLImageElement\n img.style.display = 'none'\n}\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.skill-card {\n @apply flex flex-col gap-1.5 rounded-xl border border-zinc-200 bg-white p-3 text-left transition hover:border-zinc-300 hover:shadow-sm cursor-pointer;\n}\n\n.skill-card.is-disabled {\n @apply opacity-50;\n}\n\n.skill-card-top {\n @apply flex items-start gap-2.5;\n}\n\n.skill-card-avatar {\n @apply w-8 h-8 rounded-full shrink-0 bg-zinc-100;\n}\n\n.skill-card-avatar-fallback {\n @apply w-8 h-8 rounded-full shrink-0 bg-zinc-200 text-zinc-500 flex items-center justify-center text-xs font-medium uppercase;\n}\n\n.skill-card-info {\n @apply flex flex-col gap-0.5 min-w-0 flex-1;\n}\n\n.skill-card-header {\n @apply flex items-center gap-2;\n}\n\n.skill-card-name {\n @apply text-sm font-medium text-zinc-900 truncate;\n}\n\n.skill-card-badge {\n @apply shrink-0 rounded-md border border-emerald-200 bg-emerald-50 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700 leading-none;\n}\n\n.skill-card-badge-disabled {\n @apply shrink-0 rounded-md border border-zinc-200 bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500 leading-none;\n}\n\n.skill-card-owner {\n @apply text-xs text-zinc-400;\n}\n\n.skill-card-browse {\n @apply shrink-0 ml-auto h-7 w-7 rounded-lg border-0 bg-transparent text-zinc-300 flex items-center justify-center transition hover:bg-zinc-100 hover:text-zinc-600;\n}\n\n.skill-card-browse-icon {\n @apply w-4 h-4;\n}\n\n.skill-card-desc {\n @apply m-0 text-xs text-zinc-500 line-clamp-2;\n}\n\n.skill-card-date {\n @apply text-[10px] text-zinc-300;\n}\n</style>\n","<template>\n <Teleport to=\"body\">\n <div v-if=\"visible\" class=\"sdm-overlay\" @click.self=\"$emit('close')\">\n <div class=\"sdm-panel\">\n <div class=\"sdm-header\">\n <div class=\"sdm-title-area\">\n <img\n v-if=\"skill.avatarUrl\"\n class=\"sdm-avatar\"\n :src=\"skill.avatarUrl\"\n :alt=\"skill.owner\"\n loading=\"lazy\"\n />\n <div class=\"sdm-title-col\">\n <div class=\"sdm-title-row\">\n <h3 class=\"sdm-title\">{{ skill.displayName || skill.name }}</h3>\n <span v-if=\"skill.installed && !effectiveEnabled\" class=\"sdm-badge-disabled\">Disabled</span>\n </div>\n <span class=\"sdm-owner\">{{ skill.owner }}</span>\n </div>\n </div>\n <button class=\"sdm-close\" type=\"button\" aria-label=\"Close\" @click=\"$emit('close')\">\n <IconTablerX class=\"sdm-close-icon\" />\n </button>\n </div>\n\n <div class=\"sdm-body\">\n <p v-if=\"effectiveDescription\" class=\"sdm-desc\">{{ effectiveDescription }}</p>\n\n <div v-if=\"isLoadingReadme\" class=\"sdm-readme-loading\">Loading skill contents...</div>\n <div v-else-if=\"readmeContent\" class=\"sdm-readme\" v-html=\"renderedReadme\"></div>\n\n <a class=\"sdm-link\" :href=\"skill.url\" target=\"_blank\" rel=\"noopener noreferrer\">View on GitHub</a>\n </div>\n\n <div class=\"sdm-footer\">\n <div class=\"sdm-footer-actions\">\n <button\n v-if=\"skill.installed\"\n class=\"sdm-btn sdm-btn-danger\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onUninstall\"\n >\n {{ props.isUninstalling ? 'Uninstalling...' : 'Uninstall' }}\n </button>\n <button\n v-else\n class=\"sdm-btn sdm-btn-primary\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onInstall\"\n >\n {{ props.isInstalling ? 'Installing...' : 'Install' }}\n </button>\n\n <button\n v-if=\"skill.installed\"\n class=\"sdm-btn sdm-btn-secondary\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onToggleEnabled\"\n >\n {{ effectiveEnabled ? 'Disable' : 'Enable' }}\n </button>\n\n <button\n v-if=\"skill.installed && skillDirPath\"\n class=\"sdm-btn sdm-btn-secondary\"\n type=\"button\"\n @click=\"onBrowseFiles\"\n >\n Browse files\n </button>\n </div>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport IconTablerX from '../icons/IconTablerX.vue'\n\nexport type HubSkill = {\n name: string\n owner: string\n description: string\n displayName?: string\n publishedAt?: number\n avatarUrl?: string\n url: string\n installed: boolean\n path?: string\n enabled?: boolean\n}\n\nconst props = defineProps<{\n skill: HubSkill\n visible: boolean\n isInstalling?: boolean\n isUninstalling?: boolean\n}>()\n\nconst emit = defineEmits<{\n close: []\n install: [skill: HubSkill]\n uninstall: [skill: HubSkill]\n 'toggle-enabled': [skill: HubSkill, enabled: boolean]\n}>()\n\nconst localEnabled = ref<boolean | null>(null)\nconst localDescription = ref('')\nconst readmeContent = ref('')\nconst isLoadingReadme = ref(false)\n\nconst effectiveEnabled = computed(() => localEnabled.value ?? props.skill.enabled ?? true)\nconst isActing = computed(() => (props.isInstalling === true) || (props.isUninstalling === true))\nconst effectiveDescription = computed(() => localDescription.value || props.skill.description)\nconst skillDirPath = computed(() => {\n const p = props.skill.path\n if (!p) return ''\n return p.endsWith('/SKILL.md') ? p.slice(0, -'/SKILL.md'.length) : p\n})\n\nconst renderedReadme = computed(() => {\n const raw = readmeContent.value\n if (!raw) return ''\n const withoutFrontmatter = raw.replace(/^---[\\s\\S]*?---\\s*/, '')\n return simpleMarkdown(withoutFrontmatter)\n})\n\nfunction simpleMarkdown(md: string): string {\n const escaped = md\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n return escaped\n .replace(/^### (.+)$/gm, '<h4>$1</h4>')\n .replace(/^## (.+)$/gm, '<h3>$1</h3>')\n .replace(/^# (.+)$/gm, '<h2>$1</h2>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/^- (.+)$/gm, '<li>$1</li>')\n .replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>')\n .replace(/\\n{2,}/g, '<br/><br/>')\n .replace(/\\n/g, '<br/>')\n}\n\nasync function fetchReadme(): Promise<void> {\n if (!props.skill.owner || !props.skill.name) return\n isLoadingReadme.value = true\n localDescription.value = ''\n readmeContent.value = ''\n try {\n const params = new URLSearchParams({ owner: props.skill.owner, name: props.skill.name })\n if (props.skill.installed) params.set('installed', 'true')\n if (props.skill.path) params.set('path', props.skill.path)\n const resp = await fetch(`/codex-api/skills-hub/readme?${params}`)\n if (!resp.ok) return\n const data = (await resp.json()) as { content?: string; description?: string }\n readmeContent.value = data.content ?? ''\n localDescription.value = data.description ?? ''\n } catch {\n // silently fail\n } finally {\n isLoadingReadme.value = false\n }\n}\n\nwatch(() => props.visible, (v) => {\n if (v) {\n localEnabled.value = null\n localDescription.value = ''\n readmeContent.value = ''\n void fetchReadme()\n }\n})\n\nfunction onInstall(): void {\n emit('install', props.skill)\n}\n\nfunction onUninstall(): void {\n emit('uninstall', props.skill)\n}\n\nfunction onToggleEnabled(): void {\n const next = !effectiveEnabled.value\n localEnabled.value = next\n emit('toggle-enabled', props.skill, next)\n}\n\nfunction onBrowseFiles(): void {\n const dir = skillDirPath.value\n if (!dir) return\n window.open(`/codex-local-browse${encodeURI(dir)}`, '_blank', 'noopener,noreferrer')\n}\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.sdm-overlay {\n @apply fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/40;\n}\n\n.sdm-panel {\n @apply w-full max-w-lg max-h-[90vh] sm:max-h-[80vh] rounded-t-2xl sm:rounded-2xl bg-white shadow-xl flex flex-col overflow-hidden;\n}\n\n.sdm-header {\n @apply flex items-start justify-between gap-3 p-4 sm:p-5 pb-3 shrink-0;\n}\n\n.sdm-title-area {\n @apply flex items-center gap-3 min-w-0;\n}\n\n.sdm-avatar {\n @apply w-10 h-10 rounded-full shrink-0 bg-zinc-100;\n}\n\n.sdm-title-col {\n @apply flex flex-col gap-0.5 min-w-0;\n}\n\n.sdm-title-row {\n @apply flex items-center gap-2 min-w-0;\n}\n\n.sdm-title {\n @apply text-lg font-semibold text-zinc-900 m-0 truncate;\n}\n\n.sdm-badge-disabled {\n @apply shrink-0 rounded-md border border-zinc-200 bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500 leading-none;\n}\n\n.sdm-owner {\n @apply text-xs text-zinc-400;\n}\n\n.sdm-close {\n @apply shrink-0 h-7 w-7 rounded-lg border-0 bg-transparent text-zinc-400 flex items-center justify-center transition hover:bg-zinc-100 hover:text-zinc-700;\n}\n\n.sdm-close-icon {\n @apply w-4 h-4;\n}\n\n.sdm-body {\n @apply p-4 sm:p-5 pt-0 flex flex-col gap-3 overflow-y-auto flex-1 min-h-0;\n}\n\n.sdm-desc {\n @apply m-0 text-sm text-zinc-600 leading-relaxed;\n}\n\n.sdm-readme-loading {\n @apply text-xs text-zinc-400;\n}\n\n.sdm-readme {\n @apply text-xs text-zinc-700 leading-relaxed border-t border-zinc-100 pt-3;\n}\n\n.sdm-readme :deep(h2) {\n @apply text-sm font-semibold text-zinc-800 mt-3 mb-1;\n}\n\n.sdm-readme :deep(h3) {\n @apply text-xs font-semibold text-zinc-700 mt-2 mb-1;\n}\n\n.sdm-readme :deep(h4) {\n @apply text-xs font-medium text-zinc-600 mt-2 mb-0.5;\n}\n\n.sdm-readme :deep(code) {\n @apply bg-zinc-100 rounded px-1 py-0.5 text-[11px] font-mono;\n}\n\n.sdm-readme :deep(ul) {\n @apply m-0 pl-4 list-disc;\n}\n\n.sdm-readme :deep(li) {\n @apply mb-0.5;\n}\n\n.sdm-readme :deep(strong) {\n @apply font-semibold;\n}\n\n.sdm-link {\n @apply text-xs text-blue-600 hover:text-blue-700 no-underline hover:underline shrink-0;\n}\n\n.sdm-footer {\n @apply p-4 sm:p-5 pt-3 border-t border-zinc-100 shrink-0;\n}\n\n.sdm-footer-actions {\n @apply flex items-center gap-2;\n}\n\n.sdm-btn {\n @apply rounded-lg px-3 py-1.5 text-sm font-medium transition border-0 disabled:opacity-50 disabled:cursor-not-allowed;\n}\n\n.sdm-btn-primary {\n @apply bg-zinc-900 text-white hover:bg-black;\n}\n\n.sdm-btn-danger {\n @apply bg-rose-600 text-white hover:bg-rose-700;\n}\n\n.sdm-btn-secondary {\n @apply bg-zinc-100 text-zinc-700 hover:bg-zinc-200;\n}\n</style>\n","import { computed, ref } from 'vue'\n\ntype ToastType = 'success' | 'error'\n\ntype SyncStartupStatus = {\n inProgress: boolean\n mode: string\n branch: string\n lastAction: string\n lastRunAtIso: string\n lastSuccessAtIso: string\n lastError: string\n}\n\nexport type SkillsSyncStatus = {\n loggedIn: boolean\n githubUsername: string\n repoOwner: string\n repoName: string\n configured: boolean\n startup: SyncStartupStatus\n}\n\ntype UseGithubSkillsSyncOptions = {\n showToast: (text: string, type?: ToastType) => void\n onPulled: () => Promise<void>\n}\n\nconst firebaseConfig = {\n apiKey: 'AIzaSyAf0CIHBZ-wEQJ8CCUUWo1Wl9P7typ_ZPI',\n authDomain: 'gptcall-416910.firebaseapp.com',\n projectId: 'gptcall-416910',\n storageBucket: 'gptcall-416910.appspot.com',\n messagingSenderId: '99275526699',\n appId: '1:99275526699:web:3b623e1e2996108b52106e',\n}\n\nlet firebaseGithubAuthLoader:\n Promise<[typeof import('firebase/app'), typeof import('firebase/auth')]> | null = null\n\nfunction loadFirebaseGithubAuth() {\n if (!firebaseGithubAuthLoader) {\n firebaseGithubAuthLoader = Promise.all([\n import('firebase/app'),\n import('firebase/auth'),\n ])\n }\n return firebaseGithubAuthLoader\n}\n\nexport function useGithubSkillsSync(options: UseGithubSkillsSyncOptions) {\n const deviceLogin = ref<{ device_code: string; user_code: string; verification_uri: string } | null>(null)\n const syncActionStatus = ref('')\n const syncActionError = ref('')\n const syncActionInFlight = ref<'pull' | 'push' | 'startup-sync' | ''>('')\n const syncStatus = ref<SkillsSyncStatus>({\n loggedIn: false,\n githubUsername: '',\n repoOwner: '',\n repoName: '',\n configured: false,\n startup: {\n inProgress: false,\n mode: 'idle',\n branch: 'main',\n lastAction: 'not-started',\n lastRunAtIso: '',\n lastSuccessAtIso: '',\n lastError: '',\n },\n })\n\n const isPullInFlight = computed(() => syncActionInFlight.value === 'pull')\n const isPushInFlight = computed(() => syncActionInFlight.value === 'push')\n const isStartupSyncInFlight = computed(() => syncActionInFlight.value === 'startup-sync')\n const isSyncActionInFlight = computed(() => syncActionInFlight.value !== '')\n\n async function loadSyncStatus(): Promise<void> {\n try {\n const resp = await fetch('/codex-api/skills-sync/status')\n if (!resp.ok) return\n const payload = (await resp.json()) as { data?: SkillsSyncStatus }\n if (payload.data) syncStatus.value = payload.data\n } catch {\n // best effort\n }\n }\n\n async function startGithubLogin(): Promise<void> {\n try {\n const startResp = await fetch('/codex-api/skills-sync/github/start-login', { method: 'POST' })\n const startData = (await startResp.json()) as { data?: { device_code: string; user_code: string; verification_uri: string; interval?: number } }\n if (!startResp.ok || !startData.data) throw new Error('Failed to start GitHub login')\n deviceLogin.value = startData.data\n const maxAttempts = 30\n const waitMs = Math.max((startData.data.interval ?? 5) * 1000, 3000)\n let loggedIn = false\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise((resolve) => setTimeout(resolve, waitMs))\n const completeResp = await fetch('/codex-api/skills-sync/github/complete-login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode: startData.data.device_code }),\n })\n const completeData = (await completeResp.json()) as { ok?: boolean; pending?: boolean; error?: string }\n if (!completeResp.ok) throw new Error(completeData.error || 'Failed to complete GitHub login')\n if (completeData.ok) {\n loggedIn = true\n break\n }\n if (!completeData.pending) throw new Error(completeData.error || 'Failed to complete GitHub login')\n }\n if (!loggedIn) throw new Error('GitHub login timed out. Please retry.')\n deviceLogin.value = null\n await loadSyncStatus()\n options.showToast('GitHub login successful')\n } catch (e) {\n options.showToast(e instanceof Error ? e.message : 'Failed GitHub login', 'error')\n }\n }\n\n async function startGithubFirebaseLogin(): Promise<void> {\n try {\n const [firebaseApp, firebaseAuth] = await loadFirebaseGithubAuth()\n const { getApp, getApps, initializeApp } = firebaseApp\n const { getAuth, GithubAuthProvider, signInWithPopup } = firebaseAuth\n const app = getApps().length > 0 ? getApp() : initializeApp(firebaseConfig)\n const auth = getAuth(app)\n const provider = new GithubAuthProvider()\n provider.addScope('repo')\n const result = await signInWithPopup(auth, provider)\n const credential = GithubAuthProvider.credentialFromResult(result)\n const token = credential?.accessToken ?? ''\n if (!token) {\n throw new Error('GitHub access token missing from Firebase login')\n }\n const resp = await fetch('/codex-api/skills-sync/github/token-login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) {\n throw new Error(data.error || 'Failed to login with GitHub token')\n }\n await loadSyncStatus()\n options.showToast('GitHub login successful')\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed Firebase GitHub login'\n options.showToast(message, 'error')\n }\n }\n\n async function pullSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'pull-started'\n syncActionInFlight.value = 'pull'\n try {\n const resp = await fetch('/codex-api/skills-sync/pull', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to pull synced skills')\n await options.onPulled()\n syncActionStatus.value = 'pull-success'\n options.showToast(syncStatus.value.loggedIn ? 'Pulled skills from private sync repo' : 'Pulled skills from upstream repo')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed to pull sync'\n syncActionError.value = message\n syncActionStatus.value = 'pull-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function pushSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'push-started'\n syncActionInFlight.value = 'push'\n try {\n const resp = await fetch('/codex-api/skills-sync/push', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to push synced skills')\n syncActionStatus.value = 'push-success'\n options.showToast('Pushed skills to private sync repo')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed to push sync'\n syncActionError.value = message\n syncActionStatus.value = 'push-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function startupSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'startup-sync-started'\n syncActionInFlight.value = 'startup-sync'\n try {\n const resp = await fetch('/codex-api/skills-sync/startup-sync', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to run startup sync')\n await options.onPulled()\n await loadSyncStatus()\n syncActionStatus.value = 'startup-sync-success'\n options.showToast('Startup sync completed')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed startup sync'\n syncActionError.value = message\n syncActionStatus.value = 'startup-sync-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function logoutGithub(): Promise<void> {\n try {\n const resp = await fetch('/codex-api/skills-sync/github/logout', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to logout GitHub')\n await loadSyncStatus()\n options.showToast('Logged out from GitHub')\n } catch (e) {\n options.showToast(e instanceof Error ? e.message : 'Failed to logout GitHub', 'error')\n }\n }\n\n return {\n deviceLogin,\n isPullInFlight,\n isPushInFlight,\n isStartupSyncInFlight,\n isSyncActionInFlight,\n loadSyncStatus,\n logoutGithub,\n pullSkillsSync,\n pushSkillsSync,\n startupSkillsSync,\n startGithubFirebaseLogin,\n startGithubLogin,\n syncActionError,\n syncActionStatus,\n syncStatus,\n }\n}\n","<template>\n <div class=\"skills-hub\">\n <div class=\"skills-hub-header\">\n <h2 class=\"skills-hub-title\">Skills Hub</h2>\n <p class=\"skills-hub-subtitle\">Browse and discover skills from the OpenClaw community</p>\n </div>\n\n <div class=\"skills-sync-panel\">\n <div class=\"skills-sync-header\">\n <strong>Skills Sync (GitHub)</strong>\n <a\n v-if=\"syncStatus.configured && githubRepoUrl\"\n class=\"skills-sync-badge skills-sync-badge-link\"\n :href=\"githubRepoUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Connected: {{ syncStatus.repoOwner }}/{{ syncStatus.repoName }}\n </a>\n <span v-else-if=\"syncStatus.loggedIn\" class=\"skills-sync-badge\">Logged in as {{ syncStatus.githubUsername }}</span>\n <span v-else class=\"skills-sync-badge\">Not connected</span>\n </div>\n <div class=\"skills-sync-meta\">\n <span>Startup: {{ syncStatus.startup.mode }}</span>\n <span>Branch: {{ syncStatus.startup.branch }}</span>\n <span>Action: {{ syncStatus.startup.lastAction }}</span>\n </div>\n <div v-if=\"syncStatus.startup.lastError\" class=\"skills-sync-error\">\n {{ syncStatus.startup.lastError }}\n </div>\n <div v-if=\"syncActionStatus\" class=\"skills-sync-meta\">\n <span>Manual sync: {{ syncActionStatus }}</span>\n </div>\n <div v-if=\"syncActionError\" class=\"skills-sync-error\">\n {{ syncActionError }}\n </div>\n <div v-if=\"deviceLogin\" class=\"skills-sync-device\">\n <span>Open <a :href=\"deviceLogin.verification_uri\" target=\"_blank\" rel=\"noreferrer\">GitHub device login</a> and enter code:</span>\n <code>{{ deviceLogin.user_code }}</code>\n </div>\n <div class=\"skills-sync-actions\">\n <button v-if=\"!syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"startGithubFirebaseLogin\">Login with GitHub</button>\n <button v-if=\"!syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"startGithubLogin\">Device Login</button>\n <button v-if=\"syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"logoutGithub\" :disabled=\"isSyncActionInFlight\">Logout GitHub</button>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"startupSkillsSync\" :disabled=\"isSyncActionInFlight\">{{ isStartupSyncInFlight ? 'Syncing...' : 'Startup Sync' }}</button>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"pullSkillsSync\" :disabled=\"isSyncActionInFlight\">{{ isPullInFlight ? 'Pulling...' : 'Pull' }}</button>\n <button v-if=\"syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"pushSkillsSync\" :disabled=\"!syncStatus.configured || isSyncActionInFlight\">{{ isPushInFlight ? 'Pushing...' : 'Push' }}</button>\n </div>\n </div>\n\n <div v-if=\"toast\" class=\"skills-hub-toast\" :class=\"toastClass\">{{ toast.text }}</div>\n\n <div v-if=\"filteredInstalled.length > 0\" class=\"skills-hub-section\">\n <button class=\"skills-hub-section-toggle\" type=\"button\" @click=\"isInstalledOpen = !isInstalledOpen\">\n <span class=\"skills-hub-section-title\">Installed ({{ filteredInstalled.length }})</span>\n <IconTablerChevronRight class=\"skills-hub-section-chevron\" :class=\"{ 'is-open': isInstalledOpen }\" />\n </button>\n <div v-if=\"isInstalledOpen\" class=\"skills-hub-grid\">\n <SkillCard\n v-for=\"skill in filteredInstalled\"\n :key=\"skill.name\"\n :skill=\"skill\"\n @select=\"(skill) => openDetail(skill as HubSkill)\"\n />\n </div>\n </div>\n\n <div class=\"skills-hub-toolbar\">\n <div class=\"skills-hub-search-wrap\">\n <IconTablerSearch class=\"skills-hub-search-icon\" />\n <input\n ref=\"searchRef\"\n v-model=\"query\"\n class=\"skills-hub-search\"\n type=\"text\"\n placeholder=\"Search skills... (e.g. flight, docker, react)\"\n @keyup.enter.prevent=\"onSearchSubmit\"\n />\n <button class=\"skills-hub-search-btn\" type=\"button\" @click=\"onSearchSubmit\">Search</button>\n <span v-if=\"totalCount > 0\" class=\"skills-hub-count\">{{ totalCount }} skills</span>\n </div>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"toggleSort\">\n {{ sortLabel }}\n </button>\n </div>\n\n <div class=\"skills-hub-section\">\n <div v-if=\"isLoading\" class=\"skills-hub-loading\">Loading skills...</div>\n <div v-else-if=\"error\" class=\"skills-hub-error\">{{ error }}</div>\n <template v-else>\n <div v-if=\"browseSkills.length > 0\" class=\"skills-hub-grid\">\n <SkillCard\n v-for=\"skill in browseSkills\"\n :key=\"skill.url\"\n :skill=\"skill\"\n @select=\"(skill) => openDetail(skill as HubSkill)\"\n />\n </div>\n <div v-else-if=\"activeQuery.trim()\" class=\"skills-hub-empty\">No skills found for \"{{ activeQuery }}\"</div>\n </template>\n </div>\n\n <SkillDetailModal\n :skill=\"detailSkill\"\n :visible=\"isDetailOpen\"\n :is-installing=\"isDetailInstalling\"\n :is-uninstalling=\"isDetailUninstalling\"\n @close=\"isDetailOpen = false\"\n @install=\"handleInstall\"\n @uninstall=\"handleUninstall\"\n @toggle-enabled=\"handleToggleEnabled\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport IconTablerSearch from '../icons/IconTablerSearch.vue'\nimport IconTablerChevronRight from '../icons/IconTablerChevronRight.vue'\nimport SkillCard from './SkillCard.vue'\nimport SkillDetailModal, { type HubSkill } from './SkillDetailModal.vue'\nimport { useGithubSkillsSync } from '../../composables/useGithubSkillsSync'\n\nconst EMPTY_SKILL: HubSkill = { name: '', owner: '', description: '', url: '', installed: false }\nconst SKILLS_HUB_CACHE_KEY = 'codex-web-local.skills-hub.cache.v1'\ntype SkillsHubPayload = { data: HubSkill[]; installed?: HubSkill[]; total: number }\n\nconst searchRef = ref<HTMLInputElement | null>(null)\nconst query = ref('')\nconst activeQuery = ref('')\nconst sortMode = ref<'date' | 'name'>('date')\nconst browseSkills = ref<HubSkill[]>([])\nconst installedSkills = ref<HubSkill[]>([])\nconst totalCount = ref(0)\nconst isLoading = ref(false)\nconst error = ref('')\nconst isInstalledOpen = ref(true)\nconst isDetailOpen = ref(false)\nconst detailSkill = ref<HubSkill>(EMPTY_SKILL)\nconst toast = ref<{ text: string; type: 'success' | 'error' } | null>(null)\nconst actionSkillKey = ref('')\nconst isInstallActionInFlight = ref(false)\nconst isUninstallActionInFlight = ref(false)\nlet toastTimer: ReturnType<typeof setTimeout> | null = null\n\nconst emit = defineEmits<{\n 'skills-changed': []\n}>()\n\nconst sortLabel = computed(() => sortMode.value === 'date' ? 'Newest' : 'A-Z')\nconst toastClass = computed(() => toast.value?.type === 'error' ? 'skills-hub-toast-error' : 'skills-hub-toast-success')\nconst currentDetailSkillKey = computed(() => `${detailSkill.value.owner}/${detailSkill.value.name}`)\nconst isDetailInstalling = computed(() =>\n isInstallActionInFlight.value && actionSkillKey.value === currentDetailSkillKey.value,\n)\nconst isDetailUninstalling = computed(() =>\n isUninstallActionInFlight.value && actionSkillKey.value === currentDetailSkillKey.value,\n)\nconst githubRepoUrl = computed(() => {\n if (!syncStatus.value.configured) return ''\n const owner = syncStatus.value.repoOwner.trim()\n const repo = syncStatus.value.repoName.trim()\n if (!owner || !repo) return ''\n return `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`\n})\nconst filteredInstalled = computed(() => {\n const q = query.value.toLowerCase().trim()\n if (!q) return installedSkills.value\n return installedSkills.value.filter((s) =>\n s.name.toLowerCase().includes(q) ||\n s.owner.toLowerCase().includes(q) ||\n (s.displayName ?? '').toLowerCase().includes(q),\n )\n})\n\nfunction showToast(text: string, type: 'success' | 'error' = 'success'): void {\n toast.value = { text, type }\n if (toastTimer) clearTimeout(toastTimer)\n toastTimer = setTimeout(() => { toast.value = null }, 3000)\n}\n\nfunction toggleSort(): void {\n sortMode.value = sortMode.value === 'date' ? 'name' : 'date'\n void fetchSkills(activeQuery.value)\n}\n\nfunction cacheKey(q: string): string {\n return `${sortMode.value}::${q.trim().toLowerCase()}`\n}\n\nfunction readCache(key: string): SkillsHubPayload | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(SKILLS_HUB_CACHE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw) as { byKey?: Record<string, SkillsHubPayload> }\n return parsed.byKey?.[key] ?? null\n } catch {\n return null\n }\n}\n\nfunction writeCache(key: string, payload: SkillsHubPayload): void {\n if (typeof window === 'undefined') return\n try {\n const raw = window.localStorage.getItem(SKILLS_HUB_CACHE_KEY)\n const parsed = raw ? (JSON.parse(raw) as { byKey?: Record<string, SkillsHubPayload> }) : {}\n const byKey = parsed.byKey ?? {}\n byKey[key] = payload\n window.localStorage.setItem(SKILLS_HUB_CACHE_KEY, JSON.stringify({ byKey }))\n } catch {\n // best-effort cache\n }\n}\n\nfunction clearCache(): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.removeItem(SKILLS_HUB_CACHE_KEY)\n } catch {\n // best-effort cache cleanup\n }\n}\n\nfunction applySkillsPayload(payload: SkillsHubPayload): void {\n const inst = payload.installed ?? []\n installedSkills.value = inst\n const installedNames = new Set(inst.map((s) => s.name))\n browseSkills.value = payload.data\n .map((s) => {\n if (s.installed || installedNames.has(s.name)) {\n const local = inst.find((i) => i.name === s.name)\n return { ...s, installed: true, path: local?.path ?? s.path, enabled: local?.enabled ?? s.enabled }\n }\n return s\n })\n .filter((s) => !s.installed)\n totalCount.value = payload.total\n}\n\nasync function fetchSkills(q: string): Promise<void> {\n const normalizedQuery = q.trim()\n activeQuery.value = normalizedQuery\n const key = cacheKey(normalizedQuery)\n const cached = readCache(key)\n if (cached) {\n applySkillsPayload(cached)\n }\n isLoading.value = !cached\n error.value = ''\n try {\n const params = new URLSearchParams()\n if (normalizedQuery) params.set('q', normalizedQuery)\n params.set('limit', '100')\n params.set('sort', sortMode.value)\n const resp = await fetch(`/codex-api/skills-hub?${params}`)\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`)\n const data = (await resp.json()) as SkillsHubPayload\n applySkillsPayload(data)\n writeCache(key, data)\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load skills'\n } finally {\n isLoading.value = false\n }\n}\n\nfunction onSearchSubmit(): void {\n void fetchSkills(query.value)\n}\n\nfunction openDetail(skill: HubSkill): void {\n detailSkill.value = skill\n isDetailOpen.value = true\n}\n\nasync function handleInstall(skill: HubSkill): Promise<void> {\n actionSkillKey.value = `${skill.owner}/${skill.name}`\n isInstallActionInFlight.value = true\n try {\n const resp = await fetch('/codex-api/skills-hub/install', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ owner: skill.owner, name: skill.name }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string; path?: string }\n if (!data.ok) throw new Error(data.error || 'Install failed')\n const installed = { ...skill, installed: true, path: data.path, enabled: true }\n installedSkills.value = [...installedSkills.value, installed]\n browseSkills.value = browseSkills.value.filter((s) => s.name !== skill.name)\n detailSkill.value = installed\n showToast(`${skill.displayName || skill.name} skill installed`)\n isDetailOpen.value = false\n clearCache()\n emit('skills-changed')\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to install skill', 'error')\n } finally {\n isInstallActionInFlight.value = false\n }\n}\n\nasync function handleUninstall(skill: HubSkill): Promise<void> {\n actionSkillKey.value = `${skill.owner}/${skill.name}`\n isUninstallActionInFlight.value = true\n try {\n const resp = await fetch('/codex-api/skills-hub/uninstall', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: skill.name, path: skill.path }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!data.ok) throw new Error(data.error || 'Uninstall failed')\n installedSkills.value = installedSkills.value.filter((s) => s.name !== skill.name)\n if (skill.owner !== 'local') {\n browseSkills.value = [...browseSkills.value, { ...skill, installed: false, path: undefined, enabled: undefined }]\n }\n showToast(`${skill.displayName || skill.name} skill uninstalled`)\n isDetailOpen.value = false\n clearCache()\n emit('skills-changed')\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to uninstall skill', 'error')\n } finally {\n isUninstallActionInFlight.value = false\n }\n}\n\nasync function handleToggleEnabled(skill: HubSkill, enabled: boolean): Promise<void> {\n try {\n const resp = await fetch('/codex-api/rpc', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ method: 'skills/config/write', params: { path: skill.path, enabled } }),\n })\n if (!resp.ok) throw new Error('Failed to update skill')\n await fetch('/codex-api/skills-sync/push', { method: 'POST' })\n showToast(`${skill.displayName || skill.name} skill ${enabled ? 'enabled' : 'disabled'}`)\n await fetchSkills(activeQuery.value)\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to update skill', 'error')\n }\n}\n\nconst {\n deviceLogin,\n isPullInFlight,\n isPushInFlight,\n isStartupSyncInFlight,\n isSyncActionInFlight,\n loadSyncStatus,\n logoutGithub,\n pullSkillsSync,\n pushSkillsSync,\n startupSkillsSync,\n startGithubFirebaseLogin,\n startGithubLogin,\n syncActionError,\n syncActionStatus,\n syncStatus,\n} = useGithubSkillsSync({\n showToast,\n onPulled: async () => {\n await fetchSkills(activeQuery.value)\n emit('skills-changed')\n },\n})\n\nonMounted(() => {\n void fetchSkills('')\n void loadSyncStatus()\n})\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.skills-hub {\n @apply flex flex-col gap-3 sm:gap-4 p-3 sm:p-6 max-w-4xl mx-auto w-full overflow-y-auto h-full;\n}\n\n.skills-hub-header {\n @apply flex flex-col gap-1;\n}\n\n.skills-hub-title {\n @apply text-xl sm:text-2xl font-semibold text-zinc-900 m-0;\n}\n\n.skills-hub-subtitle {\n @apply text-sm text-zinc-500 m-0;\n}\n\n.skills-hub-toolbar {\n @apply flex flex-col sm:flex-row items-stretch sm:items-center gap-2;\n}\n\n.skills-hub-search-wrap {\n @apply flex-1 flex items-center gap-2 rounded-xl border border-zinc-200 bg-white px-3 py-2 transition focus-within:border-zinc-400 focus-within:shadow-sm;\n}\n\n.skills-hub-search-icon {\n @apply w-4 h-4 text-zinc-400 shrink-0;\n}\n\n.skills-hub-search {\n @apply flex-1 min-w-0 bg-transparent text-sm text-zinc-800 placeholder-zinc-400 outline-none border-none p-0;\n}\n\n.skills-hub-search-btn {\n @apply shrink-0 rounded-md border border-zinc-200 bg-white px-2 py-1 text-xs font-medium text-zinc-600 transition hover:bg-zinc-50 hover:border-zinc-300 cursor-pointer;\n}\n\n.skills-hub-count {\n @apply text-xs text-zinc-400 whitespace-nowrap;\n}\n\n.skills-hub-sort {\n @apply shrink-0 rounded-lg border border-zinc-200 bg-white px-2.5 py-1.5 text-xs font-medium text-zinc-600 transition hover:bg-zinc-50 hover:border-zinc-300 cursor-pointer;\n}\n\n.skills-sync-panel {\n @apply rounded-xl border border-zinc-200 bg-zinc-50 p-3 flex flex-col gap-2;\n}\n\n.skills-sync-header {\n @apply flex flex-wrap items-center gap-2 text-sm text-zinc-700;\n}\n\n.skills-sync-badge {\n @apply text-xs rounded-md border border-zinc-300 bg-white px-2 py-0.5;\n}\n\n.skills-sync-badge-link {\n @apply text-zinc-700 hover:text-zinc-900 hover:border-zinc-400;\n}\n\n.skills-sync-device {\n @apply text-xs text-zinc-600 flex items-center gap-2 flex-wrap;\n}\n\n.skills-sync-meta {\n @apply text-xs text-zinc-600 flex items-center gap-3 flex-wrap;\n}\n\n.skills-sync-error {\n @apply text-xs text-rose-700 bg-rose-50 border border-rose-200 rounded-md px-2 py-1;\n}\n\n.skills-sync-actions {\n @apply flex flex-wrap gap-2;\n}\n\n.skills-hub-toast {\n @apply rounded-lg px-3 py-2 text-sm font-medium;\n}\n\n.skills-hub-toast-success {\n @apply border border-emerald-200 bg-emerald-50 text-emerald-700;\n}\n\n.skills-hub-toast-error {\n @apply border border-rose-200 bg-rose-50 text-rose-700;\n}\n\n.skills-hub-section {\n @apply flex flex-col gap-2;\n}\n\n.skills-hub-section-toggle {\n @apply flex items-center gap-1.5 border-0 bg-transparent p-0 text-sm font-medium text-zinc-600 transition hover:text-zinc-900 cursor-pointer;\n}\n\n.skills-hub-section-title {\n @apply text-sm font-medium;\n}\n\n.skills-hub-section-chevron {\n @apply w-3.5 h-3.5 transition-transform;\n}\n\n.skills-hub-section-chevron.is-open {\n @apply rotate-90;\n}\n\n.skills-hub-grid {\n @apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3;\n}\n\n.skills-hub-loading {\n @apply text-sm text-zinc-400 py-8 text-center;\n}\n\n.skills-hub-error {\n @apply text-sm text-rose-600 py-4 text-center rounded-lg border border-rose-200 bg-rose-50;\n}\n\n.skills-hub-empty {\n @apply text-sm text-zinc-400 py-8 text-center;\n}\n</style>\n"],"file":"assets/SkillsHub-xXcF9J80.js"}