martty 0.2.11
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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/bin/dsh-tui.js +68 -0
- package/cordis.patch.yml +30 -0
- package/creator/cordis.patch.yml +6 -0
- package/creator/package.json +10 -0
- package/lib/acp-client-events.js +65 -0
- package/lib/acp-client.js +114 -0
- package/lib/acp-host.js +24 -0
- package/lib/acp-session-config.js +376 -0
- package/lib/acp-session-plan.js +196 -0
- package/lib/acp-session-stats.js +239 -0
- package/lib/agent.js +64 -0
- package/lib/boot.js +119 -0
- package/lib/client-process.js +11 -0
- package/lib/client-run.js +379 -0
- package/lib/cordis-protocol.js +51 -0
- package/lib/creator-overlay.js +77 -0
- package/lib/demo-skin.js +79 -0
- package/lib/ember.js +20 -0
- package/lib/index.js +226 -0
- package/lib/inspect.js +971 -0
- package/lib/jsonrpc-line-transport.js +155 -0
- package/lib/mux.js +281 -0
- package/lib/palettes/default.json +44 -0
- package/lib/palettes/ember.json +44 -0
- package/lib/plan-view.js +92 -0
- package/lib/profile-acp-client.js +11 -0
- package/lib/right-demo.js +55 -0
- package/lib/runner.js +94 -0
- package/lib/spawn-tui.js +179 -0
- package/lib/stats-view.js +90 -0
- package/lib/tui-commands.js +144 -0
- package/lib/tui-overlay.js +252 -0
- package/lib/tui-slots.js +351 -0
- package/lib/tui-theme.js +463 -0
- package/package.json +83 -0
- package/skills/tui-plugin-development/SKILL.md +172 -0
- package/vendor/darwin-arm64/dsh-tui +0 -0
- package/vendor/darwin-x64/dsh-tui +0 -0
- package/vendor/linux-arm64/dsh-tui +0 -0
- package/vendor/linux-x64/dsh-tui +0 -0
- package/vendor/win32-x64/dsh-tui.exe +0 -0
package/lib/tui-theme.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin ABI for TUI palette packs: `ctx.tuiTheme.register(palette)`.
|
|
3
|
+
*
|
|
4
|
+
* Transport method `_dsh/cordis/tui/theme/update` is compositor-private.
|
|
5
|
+
* Plugins never call it.
|
|
6
|
+
* Registrations that happen before JSON-RPC `notify` exists are queued and
|
|
7
|
+
* flushed from `bindNotify`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'node:fs'
|
|
11
|
+
import { isAbsolute } from 'node:path'
|
|
12
|
+
import { CORDIS_METHODS } from './cordis-protocol.js'
|
|
13
|
+
|
|
14
|
+
export const name = 'tui-theme'
|
|
15
|
+
export const inject = []
|
|
16
|
+
|
|
17
|
+
const defaultPalette = JSON.parse(
|
|
18
|
+
readFileSync(new URL('./palettes/default.json', import.meta.url), 'utf8'),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export const TOKEN_NAMES = Object.freeze([
|
|
22
|
+
'bg',
|
|
23
|
+
'surface',
|
|
24
|
+
'panel',
|
|
25
|
+
'fg',
|
|
26
|
+
'fg_secondary',
|
|
27
|
+
'fg_tertiary',
|
|
28
|
+
'caption',
|
|
29
|
+
'brand',
|
|
30
|
+
'brand_soft',
|
|
31
|
+
'bubble_bg',
|
|
32
|
+
'bubble_fg',
|
|
33
|
+
'border',
|
|
34
|
+
'code_bg',
|
|
35
|
+
'ok',
|
|
36
|
+
'warn',
|
|
37
|
+
'err',
|
|
38
|
+
'hint',
|
|
39
|
+
'chip_bg',
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
const TOKEN_DESCRIPTIONS = {
|
|
43
|
+
bg: 'Full-screen background.',
|
|
44
|
+
surface: 'Raised transcript surface.',
|
|
45
|
+
panel: 'Composer, status, and chrome panels.',
|
|
46
|
+
fg: 'Primary text.',
|
|
47
|
+
fg_secondary: 'Secondary text.',
|
|
48
|
+
fg_tertiary: 'Tertiary text.',
|
|
49
|
+
caption: 'Captions and timestamps.',
|
|
50
|
+
brand: 'Brand accent.',
|
|
51
|
+
brand_soft: 'Soft brand fill.',
|
|
52
|
+
bubble_bg: 'User bubble background.',
|
|
53
|
+
bubble_fg: 'User bubble text.',
|
|
54
|
+
border: 'Borders and rules.',
|
|
55
|
+
code_bg: 'Code block background.',
|
|
56
|
+
ok: 'Success state.',
|
|
57
|
+
warn: 'Warning state.',
|
|
58
|
+
err: 'Error state.',
|
|
59
|
+
hint: 'Hint and prompt-row accent.',
|
|
60
|
+
chip_bg: 'Chip fill.',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Closed token directory for Cordis Client inspect (`Theme.listTokens`). */
|
|
64
|
+
export const TOKEN_INSPECT = Object.freeze(
|
|
65
|
+
TOKEN_NAMES.map((name) => Object.freeze({
|
|
66
|
+
name,
|
|
67
|
+
description: TOKEN_DESCRIPTIONS[name],
|
|
68
|
+
valueType: '#RRGGBB',
|
|
69
|
+
requiresLightAndDark: true,
|
|
70
|
+
})),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const HEX = /^#[0-9A-Fa-f]{6}$/
|
|
74
|
+
const PALETTE_FIELDS = new Set(['id', 'label', 'dark', 'light', 'background'])
|
|
75
|
+
const BACKGROUND_FIELDS = new Set(['source', 'fit', 'anchor', 'opacity'])
|
|
76
|
+
const BACKGROUND_SOURCE_FIELDS = new Set(['kind', 'path', 'mediaType', 'base64'])
|
|
77
|
+
const BACKGROUND_ANCHOR_FIELDS = new Set(['x', 'y'])
|
|
78
|
+
const PROTOCOL = 0
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate a palette against tui-palette protocol 0 (closed token names).
|
|
82
|
+
* @param {unknown} palette
|
|
83
|
+
* @returns {{ id: string, label: string, dark: Record<string, string>, light: Record<string, string>, background?: object }}
|
|
84
|
+
*/
|
|
85
|
+
export function validatePalette(palette) {
|
|
86
|
+
if (palette === null || typeof palette !== 'object' || Array.isArray(palette)) {
|
|
87
|
+
throw new Error('tuiTheme.register: palette must be an object')
|
|
88
|
+
}
|
|
89
|
+
const extraFields = Object.keys(palette).filter((key) => !PALETTE_FIELDS.has(key))
|
|
90
|
+
if (extraFields.length > 0) {
|
|
91
|
+
throw new Error(`tuiTheme.register: unknown field(s) ${extraFields.join(', ')}`)
|
|
92
|
+
}
|
|
93
|
+
if (typeof palette.id !== 'string' || palette.id.length < 1) {
|
|
94
|
+
throw new Error('tuiTheme.register: id must be a non-empty string')
|
|
95
|
+
}
|
|
96
|
+
if (typeof palette.label !== 'string' || palette.label.length < 1) {
|
|
97
|
+
throw new Error('tuiTheme.register: label must be a non-empty string')
|
|
98
|
+
}
|
|
99
|
+
const validated = {
|
|
100
|
+
id: palette.id,
|
|
101
|
+
label: palette.label,
|
|
102
|
+
dark: validateTokenMap(palette.dark, 'dark'),
|
|
103
|
+
light: validateTokenMap(palette.light, 'light'),
|
|
104
|
+
}
|
|
105
|
+
if (palette.background !== undefined) {
|
|
106
|
+
validated.background = validateBackground(palette.background)
|
|
107
|
+
}
|
|
108
|
+
return validated
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function exactFields(value, allowed, at) {
|
|
112
|
+
const extra = Object.keys(value).filter((key) => !allowed.has(key))
|
|
113
|
+
if (extra.length > 0) {
|
|
114
|
+
throw new Error(`tuiTheme.register: ${at} unknown field(s) ${extra.join(', ')}`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function finiteUnit(value, fallback, at) {
|
|
119
|
+
const selected = value === undefined ? fallback : value
|
|
120
|
+
if (typeof selected !== 'number' || !Number.isFinite(selected)
|
|
121
|
+
|| selected < 0 || selected > 1) {
|
|
122
|
+
throw new Error(`tuiTheme.register: ${at} must be a finite number from 0 to 1`)
|
|
123
|
+
}
|
|
124
|
+
return selected
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function validateBackground(background) {
|
|
128
|
+
if (background === null || typeof background !== 'object' || Array.isArray(background)) {
|
|
129
|
+
throw new Error('tuiTheme.register: background must be an object')
|
|
130
|
+
}
|
|
131
|
+
exactFields(background, BACKGROUND_FIELDS, 'background')
|
|
132
|
+
const source = background.source
|
|
133
|
+
if (source === null || typeof source !== 'object' || Array.isArray(source)) {
|
|
134
|
+
throw new Error('tuiTheme.register: background.source must be an object')
|
|
135
|
+
}
|
|
136
|
+
exactFields(source, BACKGROUND_SOURCE_FIELDS, 'background.source')
|
|
137
|
+
let validatedSource
|
|
138
|
+
if (source.kind === 'file') {
|
|
139
|
+
if (typeof source.path !== 'string' || !isAbsolute(source.path)) {
|
|
140
|
+
throw new Error('tuiTheme.register: background.source.path must be absolute')
|
|
141
|
+
}
|
|
142
|
+
if (source.mediaType !== undefined || source.base64 !== undefined) {
|
|
143
|
+
throw new Error('tuiTheme.register: file background source only accepts kind and path')
|
|
144
|
+
}
|
|
145
|
+
validatedSource = { kind: 'file', path: source.path }
|
|
146
|
+
} else if (source.kind === 'data') {
|
|
147
|
+
if (source.path !== undefined || source.mediaType !== 'image/png'
|
|
148
|
+
|| typeof source.base64 !== 'string' || source.base64.length === 0) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
'tuiTheme.register: data background source needs image/png mediaType and base64',
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
validatedSource = {
|
|
154
|
+
kind: 'data',
|
|
155
|
+
mediaType: 'image/png',
|
|
156
|
+
base64: source.base64,
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
throw new Error('tuiTheme.register: background.source.kind must be file or data')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const fit = background.fit ?? 'cover'
|
|
163
|
+
if (!['cover', 'contain', 'stretch'].includes(fit)) {
|
|
164
|
+
throw new Error('tuiTheme.register: background.fit must be cover, contain, or stretch')
|
|
165
|
+
}
|
|
166
|
+
const anchor = background.anchor ?? {}
|
|
167
|
+
if (anchor === null || typeof anchor !== 'object' || Array.isArray(anchor)) {
|
|
168
|
+
throw new Error('tuiTheme.register: background.anchor must be an object')
|
|
169
|
+
}
|
|
170
|
+
exactFields(anchor, BACKGROUND_ANCHOR_FIELDS, 'background.anchor')
|
|
171
|
+
return {
|
|
172
|
+
source: validatedSource,
|
|
173
|
+
fit,
|
|
174
|
+
anchor: {
|
|
175
|
+
x: finiteUnit(anchor.x, 0.5, 'background.anchor.x'),
|
|
176
|
+
y: finiteUnit(anchor.y, 0.5, 'background.anchor.y'),
|
|
177
|
+
},
|
|
178
|
+
opacity: finiteUnit(background.opacity, 1, 'background.opacity'),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function validateTokenMap(map, which) {
|
|
183
|
+
if (map === null || typeof map !== 'object' || Array.isArray(map)) {
|
|
184
|
+
throw new Error(`tuiTheme.register: ${which} must be an object`)
|
|
185
|
+
}
|
|
186
|
+
const missing = TOKEN_NAMES.filter((name) => !Object.hasOwn(map, name))
|
|
187
|
+
if (missing.length > 0) {
|
|
188
|
+
throw new Error(`tuiTheme.register: ${which} missing token(s) ${missing.join(', ')}`)
|
|
189
|
+
}
|
|
190
|
+
const extra = Object.keys(map).filter((name) => !TOKEN_NAMES.includes(name))
|
|
191
|
+
if (extra.length > 0) {
|
|
192
|
+
throw new Error(`tuiTheme.register: ${which} unknown token(s) ${extra.join(', ')}`)
|
|
193
|
+
}
|
|
194
|
+
const out = {}
|
|
195
|
+
for (const name of TOKEN_NAMES) {
|
|
196
|
+
const value = map[name]
|
|
197
|
+
if (typeof value !== 'string' || !HEX.test(value)) {
|
|
198
|
+
throw new Error(`tuiTheme.register: ${which}.${name} must be a #RRGGBB hex color`)
|
|
199
|
+
}
|
|
200
|
+
out[name] = value
|
|
201
|
+
}
|
|
202
|
+
return out
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Install `ctx.tuiTheme`. The service catalog starts with builtin `default`.
|
|
207
|
+
* `register` grows the catalog (like adding an agent preset). `/theme` and
|
|
208
|
+
* `activate` switch which pack covers. Builtin `default` is never mutated.
|
|
209
|
+
* @param {object} ctx
|
|
210
|
+
* @param {{ notify?: (method: string, params: object) => void }} [options]
|
|
211
|
+
* @returns {{ register: Function, activate: Function, list: Function, active: Function, subscribe: Function, observeSelected: Function, exportInspectTokens: Function, bindNotify: Function, flush: Function }}
|
|
212
|
+
*/
|
|
213
|
+
export function installTuiTheme(ctx, options = {}) {
|
|
214
|
+
const palettes = new Map()
|
|
215
|
+
const queue = []
|
|
216
|
+
const listeners = new Set()
|
|
217
|
+
let send = typeof options.notify === 'function' ? options.notify : undefined
|
|
218
|
+
let activeId = 'default'
|
|
219
|
+
let activationRevision = 0
|
|
220
|
+
|
|
221
|
+
const builtin = validatePalette(defaultPalette)
|
|
222
|
+
palettes.set(builtin.id, { palette: builtin, owner: undefined, loaded: true })
|
|
223
|
+
|
|
224
|
+
function emit(method, params) {
|
|
225
|
+
if (typeof send === 'function') {
|
|
226
|
+
send(method, params)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
queue.push({ method, params })
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function flush() {
|
|
233
|
+
if (typeof send !== 'function') return
|
|
234
|
+
const pending = queue.splice(0, queue.length)
|
|
235
|
+
for (const item of pending) send(item.method, item.params)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function bindNotify(fn) {
|
|
239
|
+
send = fn
|
|
240
|
+
flush()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function select(id, notifyPainter) {
|
|
244
|
+
if (typeof id !== 'string' || id.length < 1) {
|
|
245
|
+
throw new Error('tuiTheme.activate: id must be a non-empty string')
|
|
246
|
+
}
|
|
247
|
+
const entry = palettes.get(id)
|
|
248
|
+
if (entry === undefined) {
|
|
249
|
+
throw new Error(`tuiTheme.activate: palette "${id}" is not registered`)
|
|
250
|
+
}
|
|
251
|
+
if (!entry.loaded) {
|
|
252
|
+
throw new Error(`tuiTheme.activate: theme Plugin for palette "${id}" is not loaded`)
|
|
253
|
+
}
|
|
254
|
+
const { palette } = entry
|
|
255
|
+
const previousId = activeId
|
|
256
|
+
activeId = id
|
|
257
|
+
activationRevision += 1
|
|
258
|
+
if (notifyPainter) {
|
|
259
|
+
emit(CORDIS_METHODS.themeUpdate, { protocol: PROTOCOL, palette, activate: true })
|
|
260
|
+
}
|
|
261
|
+
if (previousId !== id) {
|
|
262
|
+
for (const listener of [...listeners]) {
|
|
263
|
+
try {
|
|
264
|
+
listener(id)
|
|
265
|
+
} catch {
|
|
266
|
+
// A broken Plugin subscriber must not block the registry commit.
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function activate(id) {
|
|
273
|
+
select(id, true)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function observeSelected(params) {
|
|
277
|
+
if (params === null || typeof params !== 'object' || Array.isArray(params)) {
|
|
278
|
+
throw new Error('tuiTheme.observeSelected: params must be an object')
|
|
279
|
+
}
|
|
280
|
+
if (params.protocol !== PROTOCOL) return false
|
|
281
|
+
select(params.id, false)
|
|
282
|
+
return true
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function register(palette, registerOptions = {}) {
|
|
286
|
+
return registerOwned(undefined, palette, registerOptions)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Dynamic Client runner entry: pin a palette to its owning Plugin id. */
|
|
290
|
+
function registerOwned(owner, palette, registerOptions = {}) {
|
|
291
|
+
if (owner !== undefined && (typeof owner !== 'string' || owner.length === 0)) {
|
|
292
|
+
throw new Error('tuiTheme.registerOwned: owner must be a non-empty string')
|
|
293
|
+
}
|
|
294
|
+
const validated = validatePalette(palette)
|
|
295
|
+
if (validated.id === 'default') {
|
|
296
|
+
throw new Error('tuiTheme.register: cannot replace builtin palette "default"')
|
|
297
|
+
}
|
|
298
|
+
const replace = registerOptions.replace === true
|
|
299
|
+
const retained = palettes.get(validated.id)
|
|
300
|
+
const resumesOwner = owner !== undefined
|
|
301
|
+
&& retained?.owner === owner
|
|
302
|
+
&& retained.loaded === false
|
|
303
|
+
if (retained !== undefined && !replace && !resumesOwner) {
|
|
304
|
+
throw new Error(`tuiTheme.register: palette "${validated.id}" is already registered`)
|
|
305
|
+
}
|
|
306
|
+
const cover = owner !== undefined || registerOptions.activate === true
|
|
307
|
+
let entry
|
|
308
|
+
let released = false
|
|
309
|
+
const setup = () => {
|
|
310
|
+
const previousId = cover ? activeId : undefined
|
|
311
|
+
let leaseRevision
|
|
312
|
+
entry = { palette: validated, owner, loaded: true }
|
|
313
|
+
palettes.set(validated.id, entry)
|
|
314
|
+
emit(CORDIS_METHODS.themeUpdate, {
|
|
315
|
+
protocol: PROTOCOL,
|
|
316
|
+
palette: validated,
|
|
317
|
+
activate: false,
|
|
318
|
+
loaded: true,
|
|
319
|
+
...(owner === undefined ? {} : { owner: { pluginId: owner } }),
|
|
320
|
+
})
|
|
321
|
+
if (cover) {
|
|
322
|
+
activate(validated.id)
|
|
323
|
+
leaseRevision = activationRevision
|
|
324
|
+
}
|
|
325
|
+
return () => {
|
|
326
|
+
const wasActive = activeId === validated.id
|
|
327
|
+
const ownsActivation = cover && wasActive && activationRevision === leaseRevision
|
|
328
|
+
const removed = palettes.get(validated.id) === entry
|
|
329
|
+
if (!removed) return
|
|
330
|
+
if (ownsActivation) {
|
|
331
|
+
const restoreId = previousId !== undefined && palettes.get(previousId)?.loaded === true
|
|
332
|
+
? previousId
|
|
333
|
+
: 'default'
|
|
334
|
+
activate(restoreId)
|
|
335
|
+
} else if (wasActive) {
|
|
336
|
+
activate('default')
|
|
337
|
+
}
|
|
338
|
+
if (owner === undefined) {
|
|
339
|
+
palettes.delete(validated.id)
|
|
340
|
+
emit(CORDIS_METHODS.themeRemove, { protocol: PROTOCOL, id: validated.id })
|
|
341
|
+
} else {
|
|
342
|
+
entry.loaded = false
|
|
343
|
+
emit(CORDIS_METHODS.themeUpdate, {
|
|
344
|
+
protocol: PROTOCOL,
|
|
345
|
+
palette: validated,
|
|
346
|
+
activate: false,
|
|
347
|
+
loaded: false,
|
|
348
|
+
owner: { pluginId: owner },
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const update = (patch) => {
|
|
354
|
+
if (released || entry === undefined || palettes.get(validated.id) !== entry) {
|
|
355
|
+
throw new Error(`tuiTheme.register: palette "${validated.id}" is no longer registered`)
|
|
356
|
+
}
|
|
357
|
+
if (patch === null || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
358
|
+
throw new Error('tuiTheme.register: update patch must be an object')
|
|
359
|
+
}
|
|
360
|
+
const extra = Object.keys(patch).filter((key) => key !== 'background')
|
|
361
|
+
if (extra.length > 0) {
|
|
362
|
+
throw new Error(`tuiTheme.register: update unknown field(s) ${extra.join(', ')}`)
|
|
363
|
+
}
|
|
364
|
+
if (!Object.hasOwn(patch, 'background')) {
|
|
365
|
+
throw new Error('tuiTheme.register: update needs background')
|
|
366
|
+
}
|
|
367
|
+
const next = { ...entry.palette }
|
|
368
|
+
if (patch.background === null) delete next.background
|
|
369
|
+
else next.background = validateBackground(patch.background)
|
|
370
|
+
entry.palette = next
|
|
371
|
+
emit(CORDIS_METHODS.themeUpdate, {
|
|
372
|
+
protocol: PROTOCOL,
|
|
373
|
+
palette: next,
|
|
374
|
+
activate: false,
|
|
375
|
+
loaded: true,
|
|
376
|
+
...(owner === undefined ? {} : { owner: { pluginId: owner } }),
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
let release
|
|
380
|
+
if (typeof ctx.effect === 'function') {
|
|
381
|
+
release = ctx.effect(setup, `tuiTheme.register(${JSON.stringify(validated.id)})`)
|
|
382
|
+
} else {
|
|
383
|
+
release = setup()
|
|
384
|
+
}
|
|
385
|
+
const dispose = () => {
|
|
386
|
+
if (released) return
|
|
387
|
+
released = true
|
|
388
|
+
return release?.()
|
|
389
|
+
}
|
|
390
|
+
dispose.update = update
|
|
391
|
+
dispose.dispose = dispose
|
|
392
|
+
return dispose
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function list() {
|
|
396
|
+
return [...palettes.values()].map(({ palette }) => ({
|
|
397
|
+
id: palette.id,
|
|
398
|
+
label: palette.label,
|
|
399
|
+
}))
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function active() {
|
|
403
|
+
return activeId
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function owner(id) {
|
|
407
|
+
return palettes.get(id)?.owner
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function isLoaded(id) {
|
|
411
|
+
return palettes.get(id)?.loaded === true
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function subscribe(listener) {
|
|
415
|
+
if (typeof listener !== 'function') {
|
|
416
|
+
throw new Error('tuiTheme.subscribe: listener must be a function')
|
|
417
|
+
}
|
|
418
|
+
const setup = () => {
|
|
419
|
+
listeners.add(listener)
|
|
420
|
+
return () => listeners.delete(listener)
|
|
421
|
+
}
|
|
422
|
+
const release = typeof ctx.effect === 'function'
|
|
423
|
+
? ctx.effect(setup, 'tuiTheme.subscribe')
|
|
424
|
+
: setup()
|
|
425
|
+
let disposed = false
|
|
426
|
+
return () => {
|
|
427
|
+
if (disposed) return
|
|
428
|
+
disposed = true
|
|
429
|
+
return release?.()
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function exportInspectTokens() {
|
|
434
|
+
return TOKEN_INSPECT.map((token) => ({ ...token }))
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const service = {
|
|
438
|
+
register,
|
|
439
|
+
registerOwned,
|
|
440
|
+
activate,
|
|
441
|
+
list,
|
|
442
|
+
active,
|
|
443
|
+
owner,
|
|
444
|
+
isLoaded,
|
|
445
|
+
subscribe,
|
|
446
|
+
observeSelected,
|
|
447
|
+
exportInspectTokens,
|
|
448
|
+
bindNotify,
|
|
449
|
+
flush,
|
|
450
|
+
}
|
|
451
|
+
if (typeof ctx.provide === 'function') {
|
|
452
|
+
ctx.provide('tuiTheme', service)
|
|
453
|
+
} else {
|
|
454
|
+
ctx.tuiTheme = service
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return service
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** Cordis plugin entry: provide the client-tree theme registry. */
|
|
461
|
+
export function apply(ctx) {
|
|
462
|
+
installTuiTheme(ctx)
|
|
463
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "martty",
|
|
3
|
+
"version": "0.2.11",
|
|
4
|
+
"description": "Terminal-native ACP client UI; Cordis client tree, any ACP agent",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/openma-ai/Martty.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://dshsuite.dev",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/openma-ai/Martty/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "lib/index.js",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"pretest": "node --test ../scripts/workflow-release.test.mjs ../scripts/package-alias.test.mjs",
|
|
18
|
+
"test": "node --test ../scripts/package-native.test.mjs ../scripts/check-release-tag.test.mjs ../scripts/check-static-elf.test.mjs ../scripts/smoke-old-linux.test.mjs ../scripts/cargo-guard.test.mjs ../scripts/build-npm.test.mjs ../scripts/client-profile.test.mjs ../scripts/plugin-runner.test.mjs ../scripts/profile-link-resolution.test.mjs ../scripts/release.test.mjs ../scripts/jsonrpc-line-transport.test.mjs ../scripts/tui-theme.test.mjs ../scripts/tui-slots.test.mjs ../scripts/tui-commands.test.mjs ../scripts/tui-overlay.test.mjs ../scripts/mux.test.mjs ../scripts/acp-client.test.mjs ../scripts/acp-client-events.test.mjs ../scripts/acp-session-config.test.mjs ../scripts/acp-session-plan.test.mjs ../scripts/acp-session-stats.test.mjs ../scripts/plan-view.test.mjs ../scripts/stats-view.test.mjs ../scripts/runner.test.mjs ../scripts/inspect.test.mjs ../scripts/creator-overlay.test.mjs ../scripts/real-agent-e2e.test.mjs"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public",
|
|
22
|
+
"tag": "latest"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./lib/index.js",
|
|
26
|
+
"./ember": "./lib/ember.js",
|
|
27
|
+
"./theme": "./lib/tui-theme.js",
|
|
28
|
+
"./slots": "./lib/tui-slots.js",
|
|
29
|
+
"./commands": "./lib/tui-commands.js",
|
|
30
|
+
"./overlay": "./lib/tui-overlay.js",
|
|
31
|
+
"./session-plan": "./lib/acp-session-plan.js",
|
|
32
|
+
"./session-stats": "./lib/acp-session-stats.js",
|
|
33
|
+
"./plan-view": "./lib/plan-view.js",
|
|
34
|
+
"./stats-view": "./lib/stats-view.js",
|
|
35
|
+
"./right-demo": "./lib/right-demo.js",
|
|
36
|
+
"./acp-client": "./lib/acp-client.js",
|
|
37
|
+
"./acp-client-events": "./lib/acp-client-events.js",
|
|
38
|
+
"./profile-acp-client": "./lib/profile-acp-client.js",
|
|
39
|
+
"./acp-host": "./lib/acp-host.js",
|
|
40
|
+
"./creator-overlay": "./lib/creator-overlay.js",
|
|
41
|
+
"./cordis-client-runner": "./lib/inspect.js",
|
|
42
|
+
"./runner": "./lib/runner.js",
|
|
43
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"bin": {
|
|
47
|
+
"dsh-tui": "bin/dsh-tui.js",
|
|
48
|
+
"dsb": "bin/dsh-tui.js"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"bin",
|
|
52
|
+
"lib",
|
|
53
|
+
"creator",
|
|
54
|
+
"skills/tui-plugin-development/SKILL.md",
|
|
55
|
+
"vendor",
|
|
56
|
+
"cordis.patch.yml",
|
|
57
|
+
"README.md",
|
|
58
|
+
"LICENSE"
|
|
59
|
+
],
|
|
60
|
+
"dsh": {
|
|
61
|
+
"bundle": {
|
|
62
|
+
"patch": "./cordis.patch.yml"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
67
|
+
"@openma/deepseek-harness-acp": "0.4.13"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@deepseek-ai/dsh": "0.1.0-rc.6"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=18"
|
|
74
|
+
},
|
|
75
|
+
"keywords": [
|
|
76
|
+
"deepseek",
|
|
77
|
+
"harness",
|
|
78
|
+
"tui",
|
|
79
|
+
"agent",
|
|
80
|
+
"acp",
|
|
81
|
+
"agent-client-protocol"
|
|
82
|
+
]
|
|
83
|
+
}
|