codexmate 0.0.33 → 0.0.36

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 (47) hide show
  1. package/cli/agents-files.js +6 -0
  2. package/cli/archive-helpers.js +11 -4
  3. package/cli/local-bridge.js +9 -4
  4. package/cli/openai-bridge.js +1 -1
  5. package/cli/update.js +11 -2
  6. package/cli.js +133 -64
  7. package/lib/cli-webhook.js +29 -1
  8. package/package.json +2 -1
  9. package/web-ui/app.js +37 -2
  10. package/web-ui/index.html +2 -1
  11. package/web-ui/logic.claude.mjs +4 -0
  12. package/web-ui/logic.sessions.mjs +6 -5
  13. package/web-ui/modules/app.computed.dashboard.mjs +4 -0
  14. package/web-ui/modules/app.computed.session.mjs +147 -6
  15. package/web-ui/modules/app.methods.claude-config.mjs +41 -0
  16. package/web-ui/modules/app.methods.codex-config.mjs +11 -3
  17. package/web-ui/modules/app.methods.navigation.mjs +32 -2
  18. package/web-ui/modules/app.methods.session-browser.mjs +12 -6
  19. package/web-ui/modules/app.methods.session-trash.mjs +30 -0
  20. package/web-ui/modules/app.methods.startup-claude.mjs +9 -0
  21. package/web-ui/modules/app.methods.webhook.mjs +8 -0
  22. package/web-ui/modules/i18n.dict.mjs +8 -0
  23. package/web-ui/modules/sessions-filters-url.mjs +65 -12
  24. package/web-ui/modules/skills.methods.mjs +31 -0
  25. package/web-ui/partials/index/layout-header.html +17 -12
  26. package/web-ui/partials/index/modal-webhook.html +42 -0
  27. package/web-ui/partials/index/modals-basic.html +50 -0
  28. package/web-ui/partials/index/panel-config-claude.html +13 -22
  29. package/web-ui/partials/index/panel-config-codex.html +8 -22
  30. package/web-ui/partials/index/panel-market.html +76 -149
  31. package/web-ui/partials/index/panel-sessions.html +2 -2
  32. package/web-ui/partials/index/panel-settings.html +119 -149
  33. package/web-ui/partials/index/panel-usage.html +115 -68
  34. package/web-ui/res/vue.runtime.global.prod.js +7 -0
  35. package/web-ui/res/web-ui-render.precompiled.js +7274 -0
  36. package/web-ui/session-helpers.mjs +15 -4
  37. package/web-ui/source-bundle.cjs +73 -1
  38. package/web-ui/styles/base-theme.css +10 -0
  39. package/web-ui/styles/bridge-pool.css +69 -0
  40. package/web-ui/styles/layout-shell.css +66 -27
  41. package/web-ui/styles/navigation-panels.css +8 -0
  42. package/web-ui/styles/responsive.css +50 -9
  43. package/web-ui/styles/sessions-usage.css +336 -319
  44. package/web-ui/styles/settings-panel.css +300 -234
  45. package/web-ui/styles/skills-market.css +294 -0
  46. package/web-ui/styles/titles-cards.css +14 -0
  47. package/web-ui/styles/webhook.css +38 -4
@@ -4,172 +4,99 @@
4
4
  id="panel-market"
5
5
  role="tabpanel"
6
6
  aria-labelledby="tab-market">
7
- <div class="selector-section market-overview-section">
8
- <div class="selector-header market-overview-header">
9
- <div>
10
- <span class="selector-title">{{ t('market.title') }}</span>
11
- <div class="skills-panel-note">{{ t('market.subtitle') }}</div>
12
- </div>
13
- <div class="settings-tab-actions market-header-actions">
14
- <button type="button" class="btn-tool btn-tool-compact" @click="loadSkillsMarketOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
15
- {{ skillsMarketLoading ? t('market.refreshing') : t('market.refresh') }}
16
- </button>
17
- <button type="button" class="btn-tool btn-tool-compact" @click="openSkillsManager" :disabled="loading || !!initError || skillsMarketBusy">
18
- {{ t('market.openManager') }}
7
+ <!-- Minimalist header with target switch -->
8
+ <div class="skills-minimal-header">
9
+ <div class="skills-header-left">
10
+ <span class="skills-header-title">{{ t('market.title') }}</span>
11
+ <div class="skills-target-switch" role="group" :aria-label="t('market.target.aria')">
12
+ <button
13
+ type="button"
14
+ :class="['skills-target-chip', { active: skillsTargetApp === 'codex' }]"
15
+ :aria-pressed="skillsTargetApp === 'codex'"
16
+ :disabled="loading || !!initError || skillsMarketBusy"
17
+ @click="setSkillsTargetApp('codex', { silent: false })">
18
+ Codex
19
19
  </button>
20
- <button type="button" class="btn-tool btn-tool-compact" @click="switchMainTab('dashboard')" :disabled="loading || !!initError || skillsMarketBusy">
21
- {{ t('dashboard.doctor.title') }}
20
+ <button
21
+ type="button"
22
+ :class="['skills-target-chip', { active: skillsTargetApp === 'claude' }]"
23
+ :aria-pressed="skillsTargetApp === 'claude'"
24
+ :disabled="loading || !!initError || skillsMarketBusy"
25
+ @click="setSkillsTargetApp('claude', { silent: false })">
26
+ Claude Code
22
27
  </button>
23
28
  </div>
24
29
  </div>
25
-
26
- <div class="market-target-switch" role="group" :aria-label="t('market.target.aria')">
27
- <button
28
- type="button"
29
- :class="['market-target-chip', { active: skillsTargetApp === 'codex' }]"
30
- :aria-pressed="skillsTargetApp === 'codex'"
31
- :disabled="loading || !!initError || skillsMarketBusy"
32
- @click="setSkillsTargetApp('codex', { silent: false })">
33
- Codex
30
+ <div class="skills-header-actions">
31
+ <button type="button" class="btn-icon" @click="openSkillsMenu" :aria-label="t('common.menu')" :title="t('common.menu')">
32
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
34
33
  </button>
35
- <button
36
- type="button"
37
- :class="['market-target-chip', { active: skillsTargetApp === 'claude' }]"
38
- :aria-pressed="skillsTargetApp === 'claude'"
39
- :disabled="loading || !!initError || skillsMarketBusy"
40
- @click="setSkillsTargetApp('claude', { silent: false })">
41
- Claude Code
42
- </button>
43
- </div>
44
-
45
- <div class="skills-root-box market-root-box">{{ skillsRootPath || skillsDefaultRootPath }}</div>
46
-
47
- <div class="skills-summary-strip market-summary-strip">
48
- <div class="skills-summary-item">
49
- <span class="skills-summary-label">{{ t('market.summary.target') }}</span>
50
- <strong class="skills-summary-value">{{ skillsTargetLabel }}</strong>
51
- </div>
52
- <div class="skills-summary-item">
53
- <span class="skills-summary-label">{{ t('market.summary.total') }}</span>
54
- <strong class="skills-summary-value">{{ skillsList.length }}</strong>
55
- </div>
56
- <div class="skills-summary-item">
57
- <span class="skills-summary-label">{{ t('market.summary.configured') }}</span>
58
- <strong class="skills-summary-value">{{ skillsConfiguredCount }}</strong>
59
- </div>
60
- <div class="skills-summary-item">
61
- <span class="skills-summary-label">{{ t('market.summary.missing') }}</span>
62
- <strong class="skills-summary-value">{{ skillsMissingSkillFileCount }}</strong>
63
- </div>
64
- <div class="skills-summary-item">
65
- <span class="skills-summary-label">{{ t('market.summary.importable') }}</span>
66
- <strong class="skills-summary-value">{{ skillsImportList.length }}</strong>
67
- </div>
68
- <div class="skills-summary-item">
69
- <span class="skills-summary-label">{{ t('market.summary.importableDirect') }}</span>
70
- <strong class="skills-summary-value">{{ skillsImportConfiguredCount }}</strong>
71
- </div>
72
34
  </div>
73
35
  </div>
74
36
 
75
- <div class="market-grid">
76
- <div class="skills-panel market-panel">
77
- <div class="skills-panel-header">
78
- <div class="skills-panel-title-wrap">
79
- <div class="skills-panel-title">{{ t('market.installed.title') }}</div>
80
- <div class="skills-panel-note">{{ t('market.installed.note') }}</div>
81
- </div>
82
- <button type="button" class="btn-mini" @click="refreshSkillsList({ silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
83
- {{ skillsLoading ? t('market.local.refreshing') : t('market.local.refresh') }}
84
- </button>
85
- </div>
86
- <div v-if="skillsLoading && !skillsMarketLocalLoadedOnce" class="skills-empty-state">{{ t('market.local.loading') }}</div>
87
- <div v-else-if="skillsList.length === 0" class="skills-empty-state">{{ t('market.local.empty') }}</div>
88
- <div v-else class="market-preview-list">
89
- <div v-for="skill in skillsMarketInstalledPreview" :key="'market-local-' + skill.name" class="market-preview-item">
90
- <div class="market-preview-main">
91
- <div class="market-preview-title">{{ skill.displayName || skill.name }}</div>
92
- <div class="market-preview-meta">{{ skill.description || skill.path }}</div>
93
- </div>
94
- <span :class="['pill', skill.hasSkillFile ? 'configured' : 'empty']">
95
- {{ skill.hasSkillFile ? t('market.pill.verified') : t('market.pill.missingSkill') }}
96
- </span>
97
- </div>
98
- </div>
37
+ <!-- Installed skills panel -->
38
+ <div class="skills-flow-panel">
39
+ <div class="skills-flow-header">
40
+ <div class="skills-flow-title-wrap">
41
+ <span class="skills-flow-title">{{ t('market.installed.title') }}</span>
42
+ <span class="skills-flow-count">{{ skillsList.length }}</span>
43
+ </div>
44
+ <button type="button" class="btn-mini" @click="refreshSkillsList({ silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
45
+ <svg v-if="!skillsLoading" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="14" height="14"><path d="M17 1l4 4-4 4"/><path d="M3 11V9a4 4 0 014-4h14"/><path d="M3 19l-4-4 4-4"/><path d="M17 9v2a4 4 0 01-4 4H3"/></svg>
46
+ <svg v-else viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="14" height="14" class="spin"><path d="M10 2v4m0 8v4M2 10h4m8 0h4m-3.5-6.5l2.5 2.5M4 4l2.5 2.5M16 16l-2.5-2.5M4 16l2.5-2.5"/></svg>
47
+ </button>
99
48
  </div>
100
-
101
- <div class="skills-panel market-panel">
102
- <div class="skills-panel-header">
103
- <div class="skills-panel-title-wrap">
104
- <div class="skills-panel-title">{{ t('market.import.title') }}</div>
105
- <div class="skills-panel-note">{{ t('market.import.note', { target: skillsTargetLabel }) }}</div>
106
- </div>
107
- <button type="button" class="btn-mini" @click="scanImportableSkills({ silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
108
- {{ skillsScanningImports ? t('market.import.scanning') : t('market.import.scan') }}
109
- </button>
110
- </div>
111
- <div v-if="skillsScanningImports && !skillsMarketImportLoadedOnce" class="skills-empty-state">{{ t('market.import.loading') }}</div>
112
- <div v-else-if="skillsImportList.length === 0" class="skills-empty-state">{{ t('market.import.empty') }}</div>
113
- <div v-else class="market-preview-list">
114
- <div v-for="skill in skillsMarketImportPreview" :key="'market-import-' + buildSkillImportKey(skill)" class="market-preview-item">
115
- <div class="market-preview-main">
116
- <div class="market-preview-title">{{ skill.displayName || skill.name }}</div>
117
- <div class="market-preview-meta">{{ skill.sourceLabel }} · {{ skill.sourcePath }}</div>
118
- </div>
119
- <span :class="['pill', skill.hasSkillFile ? 'configured' : 'empty']">
120
- {{ skill.hasSkillFile ? t('market.pill.importableDirect') : t('market.pill.importMissing') }}
121
- </span>
49
+ <div v-if="skillsLoading && !skillsMarketLocalLoadedOnce" class="skills-flow-loading">{{ t('market.local.loading') }}</div>
50
+ <div v-else-if="skillsList.length === 0" class="skills-flow-empty">{{ t('market.local.empty') }}</div>
51
+ <div v-else class="skills-flow-list">
52
+ <div v-for="skill in skillsList" :key="'skill-' + skill.name" class="skills-flow-item" :class="{ 'has-issue': !skill.hasSkillFile }">
53
+ <div class="skills-flow-main">
54
+ <span class="skills-flow-name">{{ skill.displayName || skill.name }}</span>
55
+ <span class="skills-flow-path">{{ skill.path }}</span>
122
56
  </div>
57
+ <span :class="['skills-flow-status', skill.hasSkillFile ? 'success' : 'warning']">
58
+ {{ skill.hasSkillFile ? t('market.pill.verified') : t('market.pill.missingSkill') }}
59
+ </span>
123
60
  </div>
124
61
  </div>
62
+ </div>
125
63
 
126
- <div class="skills-panel market-panel market-actions-panel">
127
- <div class="skills-panel-header">
128
- <div class="skills-panel-title-wrap">
129
- <div class="skills-panel-title">{{ t('market.actions.title') }}</div>
130
- <div class="skills-panel-note">{{ t('market.actions.note') }}</div>
131
- </div>
132
- </div>
133
- <div class="market-action-grid">
134
- <button type="button" class="market-action-card" @click="openSkillsManager" :disabled="loading || !!initError || skillsMarketBusy">
135
- <span class="market-action-title"><svg style="width:14px;height:14px;vertical-align:-2px;margin-right:4px" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="14" height="14" rx="3"/><path d="M7 7h6M7 10h4"/></svg>{{ t('market.action.manage.title') }}</span>
136
- <span class="market-action-copy">{{ t('market.action.manage.copy', { target: skillsTargetLabel }) }}</span>
137
- </button>
138
- <button type="button" class="market-action-card" @click="scanImportableSkills({ silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
139
- <span class="market-action-title"><svg style="width:14px;height:14px;vertical-align:-2px;margin-right:4px" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12l3-3 3 3"/><path d="M7 3v6"/><path d="M13 7l3 3 3-3"/><path d="M16 16V7"/></svg>{{ t('market.action.crossImport.title') }}</span>
140
- <span class="market-action-copy">{{ t('market.action.crossImport.copy', { target: skillsTargetLabel }) }}</span>
64
+ <!-- Importable skills panel -->
65
+ <div class="skills-flow-panel">
66
+ <div class="skills-flow-header">
67
+ <div class="skills-flow-title-wrap">
68
+ <span class="skills-flow-title">{{ t('market.import.title') }}</span>
69
+ <span class="skills-flow-count">{{ skillsImportList.length }}</span>
70
+ </div>
71
+ <button type="button" class="btn-mini" @click="scanImportableSkills({ silent: false })" :disabled="loading || !!initError || skillsMarketBusy">
72
+ <svg v-if="!skillsScanningImports" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="14" height="14"><path d="M17 1l4 4-4 4"/><path d="M3 11V9a4 4 0 014-4h14"/><path d="M3 19l-4-4 4-4"/><path d="M17 9v2a4 4 0 01-4 4H3"/></svg>
73
+ <svg v-else viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="14" height="14" class="spin"><path d="M10 2v4m0 8v4M2 10h4m8 0h4m-3.5-6.5l2.5 2.5M4 4l2.5 2.5M16 16l-2.5-2.5M4 16l2.5-2.5"/></svg>
74
+ </button>
75
+ </div>
76
+ <div v-if="skillsScanningImports && !skillsMarketImportLoadedOnce" class="skills-flow-loading">{{ t('market.import.loading') }}</div>
77
+ <div v-else-if="skillsImportList.length === 0" class="skills-flow-empty">{{ t('market.import.empty') }}</div>
78
+ <div v-else>
79
+ <!-- Quick actions -->
80
+ <div class="skills-flow-actions">
81
+ <button type="button" class="skills-action-btn" @click="openSkillsManager" :disabled="loading || !!initError || skillsMarketBusy">
82
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="16" height="16"><rect x="3" y="3" width="14" height="14" rx="3"/><path d="M7 7h6M7 10h4"/></svg>
83
+ {{ t('market.action.manage.title') }}
141
84
  </button>
142
- <button type="button" class="market-action-card" @click="triggerSkillsZipImport" :disabled="loading || !!initError || skillsMarketBusy">
143
- <span class="market-action-title"><svg style="width:14px;height:14px;vertical-align:-2px;margin-right:4px" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 14l3-3 3 3"/><path d="M7 4v7"/><rect x="12" y="4" width="5" height="12" rx="1"/></svg>{{ t('market.action.zipImport.title') }}</span>
144
- <span class="market-action-copy">{{ t('market.action.zipImport.copy') }}</span>
85
+ <button type="button" class="skills-action-btn" @click="triggerSkillsZipImport" :disabled="loading || !!initError || skillsMarketBusy">
86
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="16" height="16"><path d="M4 14l3-3 3 3"/><path d="M7 4v7"/><rect x="12" y="4" width="5" height="12" rx="1"/></svg>
87
+ {{ t('market.action.zipImport.title') }}
145
88
  </button>
146
89
  </div>
147
- </div>
148
-
149
- <div class="skills-panel market-panel market-panel-wide">
150
- <div class="skills-panel-header">
151
- <div class="skills-panel-title-wrap">
152
- <div class="skills-panel-title">{{ t('market.help.title') }}</div>
153
- </div>
154
- </div>
155
- <div class="market-preview-list">
156
- <div class="market-preview-item">
157
- <div class="market-preview-main">
158
- <div class="market-preview-title">{{ t('market.help.target.title') }}</div>
159
- <div class="market-preview-meta">{{ t('market.help.target.copy', { target: skillsTargetLabel }) }}</div>
160
- </div>
161
- </div>
162
- <div class="market-preview-item">
163
- <div class="market-preview-main">
164
- <div class="market-preview-title">{{ t('market.help.crossImport.title') }}</div>
165
- <div class="market-preview-meta">{{ t('market.help.crossImport.copy') }}</div>
166
- </div>
167
- </div>
168
- <div class="market-preview-item">
169
- <div class="market-preview-main">
170
- <div class="market-preview-title">{{ t('market.help.zipImport.title') }}</div>
171
- <div class="market-preview-meta">{{ t('market.help.zipImport.copy') }}</div>
90
+ <!-- Import list -->
91
+ <div class="skills-flow-list">
92
+ <div v-for="skill in skillsImportList" :key="'import-' + buildSkillImportKey(skill)" class="skills-flow-item">
93
+ <div class="skills-flow-main">
94
+ <span class="skills-flow-name">{{ skill.displayName || skill.name }}</span>
95
+ <span class="skills-flow-meta">{{ skill.sourceLabel }}</span>
172
96
  </div>
97
+ <button type="button" class="skills-flow-add" @click="importSingleSkill(skill)" :disabled="loading || !!initError || skillsMarketBusy">
98
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" width="16" height="16"><path d="M10 4v6m3-3l-3 3-3-3"/></svg>
99
+ </button>
173
100
  </div>
174
101
  </div>
175
102
  </div>
@@ -193,7 +193,7 @@
193
193
  </div>
194
194
  <div class="session-item-meta">
195
195
  <span class="session-source" :data-source="session.source">{{ session.sourceLabel }}</span>
196
- <span class="session-item-time">{{ session.updatedAt || t('sessions.unknownTime') }}</span>
196
+ <span class="session-item-time">{{ session.updatedAtLabel || session.updatedAt || t('sessions.unknownTime') }}</span>
197
197
  <span v-if="getSessionHotLabel(session)" class="session-item-hot">{{ getSessionHotLabel(session) }}</span>
198
198
  <span v-if="session.cwd" class="session-item-cwd session-item-sub">{{ session.cwd }}</span>
199
199
  <div v-if="session.match && session.match.snippets && session.match.snippets.length" class="session-match-snippets">
@@ -212,7 +212,7 @@
212
212
  <div class="session-preview-title">{{ activeSession.title || activeSession.sessionId }}</div>
213
213
  <div class="session-preview-meta">
214
214
  <span class="session-preview-meta-item session-source" :data-source="activeSession.source">{{ activeSession.sourceLabel }}</span>
215
- <span class="session-preview-meta-item">{{ activeSession.updatedAt || t('sessions.unknownTime') }}</span>
215
+ <span class="session-preview-meta-item">{{ activeSession.updatedAtLabel || activeSession.updatedAt || t('sessions.unknownTime') }}</span>
216
216
  </div>
217
217
  <div class="session-preview-meta" v-if="activeSession.cwd">
218
218
  <span class="session-preview-meta-item">{{ activeSession.cwd }}</span>