@toddzheng024/dscode-desktop 0.7.34 → 0.7.35
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/README.md +8 -7
- package/cordis.patch.yml +5 -4
- package/docs/CHANGELOG.md +10 -1
- package/docs/browser-use.md +34 -18
- package/docs/custom-providers.md +3 -3
- package/docs/demo.md +42 -58
- package/docs/hub-distribution.md +14 -9
- package/docs/plugin-hub.md +26 -59
- package/docs/releases/0.7.35.md +31 -0
- package/docs/triggers.md +1 -1
- package/locale/en.json +1 -1
- package/locale/zh-cn.json +1 -1
- package/package.json +3 -8
- package/plugins/browser/desktop-client.mjs +22 -9
- package/plugins/browser/desktop-host.mjs +5 -0
- package/plugins/credentials/index.mjs +11 -0
- package/plugins/custom/desktop-client.mjs +12 -9
- package/plugins/desktop/client.mjs +135 -161
- package/plugins/desktop/desktop-client.mjs +50 -0
- package/plugins/desktop/index.mjs +1 -0
- package/plugins/desktop/settings.css +36 -0
- package/plugins/dscode/desktop-client.mjs +3 -3
- package/plugins/email/desktop-client.mjs +5 -5
- package/plugins/providers/desktop-client.mjs +22 -15
- package/plugins/session-metrics/desktop-client.mjs +3 -3
- package/plugins/triggers/desktop-client.mjs +15 -9
- package/plugins/hub/catalog.mjs +0 -82
- package/plugins/hub/desktop-client.mjs +0 -114
- package/plugins/hub/desktop-host.mjs +0 -129
|
@@ -6,7 +6,7 @@ components.push({
|
|
|
6
6
|
id: '@toddzheng024/dscode-desktop',
|
|
7
7
|
factory: require => {
|
|
8
8
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
9
|
-
const control = {
|
|
9
|
+
const control = { boxSizing: 'border-box', minWidth: 0, maxWidth: '100%' };
|
|
10
10
|
const row = { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' };
|
|
11
11
|
const resetModel = model => {
|
|
12
12
|
const next = { ...model, thinking: 'default', inputModalities: ['text'] };
|
|
@@ -52,10 +52,10 @@ components.push({
|
|
|
52
52
|
h('input', { 'aria-label': label, style: { ...control, width: '100%' }, value: value ?? '', onChange: event => onChange(event.target.value), ...options }));
|
|
53
53
|
const choice = (label, value, choices, onChange) => h('label', { style: { display: 'grid', gap: 5, flex: 1 } }, label,
|
|
54
54
|
h('select', { 'aria-label': label, value, onChange: event => onChange(event.target.value), style: control }, ...choices.map(([id, text]) => h('option', { key: id, value: id }, text))));
|
|
55
|
-
return h('section', { 'aria-label': 'DSCODE model settings', style: { padding:
|
|
56
|
-
h('h2', { style: { margin: 0 } }, '
|
|
55
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE model settings', style: { padding: 0, display: 'grid', gap: 16, maxWidth: 850, color: 'inherit' } },
|
|
56
|
+
h('h2', { style: { margin: 0 } }, 'Models'),
|
|
57
57
|
h('p', { style: { margin: 0 } }, 'Connect a private or self-hosted model service. Saved models appear in the model picker.'),
|
|
58
|
-
error && h('div', { role: 'alert', style: { color: '
|
|
58
|
+
error && h('div', { role: 'alert', style: { color: 'var(--dsw-alias-state-error-primary)', whiteSpace: 'pre-wrap' } }, error),
|
|
59
59
|
notice && h('div', { role: 'status' }, notice),
|
|
60
60
|
busy && request.current && h('button', { type: 'button', style: control, disabled: request.current.signal.aborted,
|
|
61
61
|
onClick: () => { request.current?.abort(); setNotice('Cancelling request…'); },
|
|
@@ -88,8 +88,8 @@ components.push({
|
|
|
88
88
|
change({ backend: found.backend, models });
|
|
89
89
|
setNotice(`Found ${found.models.length} models. Confirm the context size and image support before saving.`);
|
|
90
90
|
}, false, true), button('Add model', async () => change({ models: [...draft.models, { id: '', thinking: 'default', inputModalities: ['text'] }] }))),
|
|
91
|
-
...draft.models.map((model, index) => h('fieldset', { key: index, style: { border: '
|
|
92
|
-
h('
|
|
91
|
+
...draft.models.map((model, index) => h('fieldset', { className: 'dscode-card', key: index, style: { border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 14, display: 'grid', gap: 12 } },
|
|
92
|
+
h('h3', null, `Model ${index + 1}`),
|
|
93
93
|
h('div', { style: row }, input(`Model ${index + 1} ID`, model.id, id => change({ models: draft.models.map((entry, i) => i === index ? { ...resetModel(entry), id } : entry) })),
|
|
94
94
|
input(`Model ${index + 1} context`, model.contextWindow, value => modelChange(index, { contextWindow: value ? Number(value) : undefined, contextSource: 'user' }), { type: 'number', min: 1, step: 1 }),
|
|
95
95
|
input(`Model ${index + 1} output limit`, model.maxTokens, value => modelChange(index, { maxTokens: value ? Number(value) : undefined, outputSource: 'user' }), { type: 'number', min: 1, step: 1 })),
|
|
@@ -118,8 +118,7 @@ components.push({
|
|
|
118
118
|
}
|
|
119
119
|
return {
|
|
120
120
|
inject: ['slots', 'connection'],
|
|
121
|
-
|
|
122
|
-
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-models', order: 11, label: () => 'DSCODE models',
|
|
121
|
+
settingsPage(ctx) { return { entry: { name: 'settings.section', id: 'dscode-models', order: 11, label: () => 'Models',
|
|
123
122
|
inject: () => ({ execute: async (action, args = {}, signal) => {
|
|
124
123
|
signal?.throwIfAborted();
|
|
125
124
|
const requestId = signal ? crypto.randomUUID() : undefined;
|
|
@@ -146,7 +145,11 @@ components.push({
|
|
|
146
145
|
if (!response.ok) throw Error(response.error.message);
|
|
147
146
|
return response.value;
|
|
148
147
|
} }),
|
|
149
|
-
}, Settings
|
|
148
|
+
}, Component: Settings }; },
|
|
149
|
+
apply(ctx, options = {}) {
|
|
150
|
+
if (options.settings === false) return;
|
|
151
|
+
const page = this.settingsPage(ctx);
|
|
152
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register(page.entry, page.Component));
|
|
150
153
|
},
|
|
151
154
|
};
|
|
152
155
|
},
|
|
@@ -158,7 +161,7 @@ components.push({
|
|
|
158
161
|
factory: require => {
|
|
159
162
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
160
163
|
const id = '@toddzheng024/dscode-browser-desktop';
|
|
161
|
-
const button = { padding: '7px 11px', border: '
|
|
164
|
+
const button = { padding: '7px 11px', border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)', background: 'transparent', color: 'inherit', cursor: 'pointer', font: 'inherit' };
|
|
162
165
|
function previewButton(props, label) {
|
|
163
166
|
return h('button', { ...props, style: { ...button, ...props.style, ...(props.disabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}) } }, label);
|
|
164
167
|
}
|
|
@@ -351,9 +354,9 @@ components.push({
|
|
|
351
354
|
setNotice('Browser control resumed. Your draft is kept. Capture a new preview before sending.');
|
|
352
355
|
await list(current, { preserveDraft: true });
|
|
353
356
|
});
|
|
354
|
-
return h('section', { 'aria-label': 'DSCODE browser preview', style: { padding: 12, boxSizing: 'border-box', overflow: 'auto', height: '100%', display: 'flex', flexDirection: 'column', gap: 8, color: 'inherit' } },
|
|
357
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE browser preview', style: { padding: 12, boxSizing: 'border-box', overflow: 'auto', height: '100%', display: 'flex', flexDirection: 'column', gap: 8, color: 'inherit' } },
|
|
355
358
|
h('strong', null, 'Browser preview'),
|
|
356
|
-
handoff && h('div', { 'aria-label': 'Browser handoff', role: 'status', style: { border: '
|
|
359
|
+
handoff && h('div', { 'aria-label': 'Browser handoff', role: 'status', style: { border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 8, padding: 10, overflowWrap: 'anywhere' } },
|
|
357
360
|
h('strong', null, 'Waiting for you'),
|
|
358
361
|
h('p', null, handoff.reason),
|
|
359
362
|
handoff.invalidated && h('p', null, 'Chrome restarted. The original tab is unavailable; choose a live tab after resuming.'),
|
|
@@ -367,7 +370,7 @@ components.push({
|
|
|
367
370
|
previewButton({ style: button, disabled: busy || controlBusy || !pageId || !!handoff, onClick: () => run(refresh) }, 'Refresh preview'),
|
|
368
371
|
frame && previewButton({ style: button, onClick: () => setExpanded(!expanded) }, expanded ? 'Fit image' : 'Expand image')),
|
|
369
372
|
h('select', { 'aria-label': 'Browser tab', value: pageId, disabled: busy || controlBusy, onChange: e => { setPageId(e.target.value); setFrame(null); setPoint(null); setText(''); setLive(false); }, style: { ...button, width: '100%' } }, h('option', { value: '' }, 'Choose a tab'), ...pages.map(page => h('option', { key: page.id, value: String(page.id) }, `${page.id} · ${page.url}`))),
|
|
370
|
-
origin && h('details', { 'aria-label': 'Browser site permissions', style: { border: '
|
|
373
|
+
origin && h('details', { 'aria-label': 'Browser site permissions', style: { border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 8, padding: '8px 10px', margin: 0, minWidth: 0 } },
|
|
371
374
|
h('summary', { 'aria-label': `Site permissions · ${access}`, style: { cursor: 'pointer' } }, 'Site permissions · ', h('span', { 'aria-label': 'Site access status' }, access)),
|
|
372
375
|
h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', margin: '8px 0' } }, origin),
|
|
373
376
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
|
|
@@ -386,7 +389,7 @@ components.push({
|
|
|
386
389
|
frame && h('div', null,
|
|
387
390
|
h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', marginBottom: 8 } }, `${frame.url} · ${new Date(frame.capturedAt).toLocaleTimeString()}`),
|
|
388
391
|
h('div', { style: { position: 'relative', lineHeight: 0, display: 'inline-block', maxWidth: '100%', ...(expanded ? { width: '100%' } : {}) } },
|
|
389
|
-
h('img', { src: `data:${frame.image.mimeType};base64,${frame.image.data}`, alt: expired ? 'Expired browser preview; refresh before annotating' : 'Captured browser page; click a point to annotate', role: 'button', tabIndex: 0, 'aria-disabled': busy || controlBusy || expired, 'aria-description': 'Enter or Space selects the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point.', onClick: selectPoint, onKeyDown: selectPointKey, style: { maxWidth: '100%', maxHeight: expanded ? 'none' : '30vh', width: expanded ? '100%' : 'auto', height: 'auto', display: 'block', cursor: expired ? 'default' : 'crosshair', borderRadius:
|
|
392
|
+
h('img', { src: `data:${frame.image.mimeType};base64,${frame.image.data}`, alt: expired ? 'Expired browser preview; refresh before annotating' : 'Captured browser page; click a point to annotate', role: 'button', tabIndex: 0, 'aria-disabled': busy || controlBusy || expired, 'aria-description': 'Enter or Space selects the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point.', onClick: selectPoint, onKeyDown: selectPointKey, style: { maxWidth: '100%', maxHeight: expanded ? 'none' : '30vh', width: expanded ? '100%' : 'auto', height: 'auto', display: 'block', cursor: expired ? 'default' : 'crosshair', borderRadius: 'var(--dsw-radius-md, 6px)' } }),
|
|
390
393
|
point && h('span', { 'aria-label': 'Selected annotation point', style: { position: 'absolute', left: `${point.x * 100}%`, top: `${point.y * 100}%`, width: 20, height: 20, border: '3px solid #f97316', borderRadius: '50%', transform: 'translate(-50%, -50%)', pointerEvents: 'none', boxShadow: '0 0 0 2px white' } }))),
|
|
391
394
|
h('p', { style: { fontSize: 12, margin: 0, opacity: 0.75 } }, 'Preview shows the controlled Chrome tab. Click a point, or focus the image and press Enter or Space to select the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point. Refresh after navigation or after one minute.'),
|
|
392
395
|
frame && point && h('p', { 'aria-label': 'Annotation coordinates', 'aria-live': 'polite', style: { fontSize: 12, margin: 0 } }, `Selected pixel: (${Math.round(point.x * (frame.width - 1))}, ${Math.round(point.y * (frame.height - 1))}) in ${frame.width}×${frame.height}.`),
|
|
@@ -413,18 +416,31 @@ components.push({
|
|
|
413
416
|
const draftRevision = useRef(0);
|
|
414
417
|
return visible ? h(SessionPreview, { execute, draft: { pageId, setPageId, text, setText, draftRevision } }) : null;
|
|
415
418
|
}
|
|
416
|
-
function
|
|
419
|
+
function PresetPreview({ execute, visible }) {
|
|
420
|
+
const [state, setState] = useState(null);
|
|
421
|
+
useEffect(() => {
|
|
422
|
+
let active = true;
|
|
423
|
+
execute('availability').then(value => { if (active) setState(value); }, error => { if (active) setState({ error: error.message }); });
|
|
424
|
+
return () => { active = false; };
|
|
425
|
+
}, [execute]);
|
|
426
|
+
if (!state) return h('p', { role: 'status' }, 'Checking DSCODE session…');
|
|
427
|
+
if (state.error) return h('p', { role: 'alert' }, state.error);
|
|
428
|
+
if (!state.available) return h('section', { className: 'dscode-ui', style: { padding: 16 } }, h('h3', null, 'DSCODE Browser preview'),
|
|
429
|
+
h('p', null, 'This session uses another preset. Create a new session and select DSCODE to use this preview. Its original browser tools remain available.'));
|
|
430
|
+
return h(SessionPane, { execute, visible });
|
|
431
|
+
}
|
|
432
|
+
function Preview({ sessionId, execute, useTabInfo, presetOnly }) {
|
|
417
433
|
const { tab } = useTabInfo();
|
|
418
434
|
// Session changes discard the draft. Hidden panes discard their request
|
|
419
435
|
// lock and preview, while late responses stay bound to the original session.
|
|
420
|
-
return h(SessionPane, { key: sessionId, execute, visible: tab.visible });
|
|
436
|
+
return h(presetOnly ? PresetPreview : SessionPane, { key: sessionId, execute, visible: tab.visible });
|
|
421
437
|
}
|
|
422
438
|
return {
|
|
423
439
|
inject: ['slots', 'sidebarRightTabs', 'connection'],
|
|
424
|
-
apply(ctx) {
|
|
440
|
+
apply(ctx, options = {}) {
|
|
425
441
|
ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-browser', multiple: false, keepMounted: true, title: () => 'Browser preview', guide: [{ id: 'open', order: 31, title: () => 'Browser preview', description: () => 'Inspect the controlled Chrome tab and send a visual annotation.' }] }), 'dscode-browser-preview.type');
|
|
426
442
|
ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
|
|
427
|
-
inject: sessionId => ({ sessionId, execute: async (action, args = {}) => {
|
|
443
|
+
inject: sessionId => ({ sessionId, presetOnly: options.presetOnly === true, execute: async (action, args = {}) => {
|
|
428
444
|
const response = await ctx.connection.rpc.call('/api', 'dscode-browser', { sessionId, action, ...args });
|
|
429
445
|
if (!response.ok) throw Error(response.error.message);
|
|
430
446
|
return response.value;
|
|
@@ -439,7 +455,7 @@ components.push({
|
|
|
439
455
|
id: '@toddzheng024/dscode-desktop',
|
|
440
456
|
factory: require => {
|
|
441
457
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
442
|
-
const control = {
|
|
458
|
+
const control = { boxSizing: 'border-box', minWidth: 0, maxWidth: '100%' };
|
|
443
459
|
function Accounts({ execute }) {
|
|
444
460
|
const [state, setState] = useState(null), [key, setKey] = useState(''), [busy, setBusy] = useState(false);
|
|
445
461
|
const [error, setError] = useState(''), [notice, setNotice] = useState(''), [confirm, setConfirm] = useState(null);
|
|
@@ -480,27 +496,28 @@ components.push({
|
|
|
480
496
|
}, [state?.opencode.pending]);
|
|
481
497
|
const button = (text, action, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: action }, text);
|
|
482
498
|
const destructive = (id, label) => button(confirm === id ? `Confirm ${label.toLowerCase()}` : label, () => confirm === id ? run(id) : setConfirm(id));
|
|
483
|
-
return h('section', { 'aria-label': 'DSCODE account settings', style: { padding:
|
|
484
|
-
h('h2', { style: { margin: 0 } }, '
|
|
499
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE account settings', style: { padding: 0, display: 'grid', gap: 16, maxWidth: 720 } },
|
|
500
|
+
h('h2', { style: { margin: 0 } }, 'Accounts'),
|
|
485
501
|
error && h('div', { role: 'alert' }, error),
|
|
486
502
|
refreshError && h('div', { role: 'alert' }, refreshError),
|
|
487
503
|
notice && h('div', { role: 'status', style: { whiteSpace: 'pre-wrap' } }, notice),
|
|
488
|
-
button('Refresh account status', () => run('status')),
|
|
489
|
-
state && h('fieldset', { disabled: busy, style: { border: '
|
|
490
|
-
h('
|
|
504
|
+
h('div', { className: 'dscode-actions' }, button('Refresh account status', () => run('status'))),
|
|
505
|
+
state && h('fieldset', { className: 'dscode-card', disabled: busy, style: { border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 16 } },
|
|
506
|
+
h('h3', null, 'DSCODE OpenRouter'),
|
|
491
507
|
h('p', null, state.openrouter.configured ? `Configured (${state.openrouter.source ?? 'saved'})` : 'No API key saved'),
|
|
492
|
-
h('p', null, '
|
|
508
|
+
h('p', null, 'Uses your existing Desktop key. Choose DSCODE OpenRouter in the model picker for DSCODE reasoning controls.'),
|
|
509
|
+
confirm === 'remove-openrouter' && h('p', { role: 'alert' }, 'Removal clears the Desktop key and any legacy terminal copy. Environment keys are unchanged.'),
|
|
493
510
|
h('label', null, 'OpenRouter API key', h('input', { 'aria-label': 'OpenRouter API key', type: 'password', autoComplete: 'new-password', style: { ...control, display: 'block', width: '100%', boxSizing: 'border-box' },
|
|
494
511
|
value: key, disabled: !state.openrouter.writable, onChange: event => { setKey(event.target.value); setConfirm(null); } })),
|
|
495
512
|
h('div', { style: { display: 'flex', gap: 8, marginTop: 12 } },
|
|
496
513
|
button('Save OpenRouter key', () => run('save-openrouter', { key }), !state.openrouter.writable || !key.trim()),
|
|
497
514
|
state.openrouter.configured && state.openrouter.writable && destructive('remove-openrouter', 'Remove OpenRouter key'))),
|
|
498
|
-
state && h('fieldset', { disabled: busy, style: { border: '
|
|
499
|
-
h('
|
|
515
|
+
state && h('fieldset', { className: 'dscode-card', disabled: busy, style: { border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 16 } },
|
|
516
|
+
h('h3', null, 'Grok'),
|
|
500
517
|
h('p', null, state.grok.configured ? 'Local Grok CLI login available' : 'No usable local Grok CLI login'),
|
|
501
518
|
h('p', null, 'Run grok login in a terminal, then refresh this status. DSCODE reads ~/.grok/auth.json without changing or refreshing its tokens.')),
|
|
502
|
-
state && h('fieldset', { disabled: busy, style: { border: '
|
|
503
|
-
h('
|
|
519
|
+
state && h('fieldset', { className: 'dscode-card', disabled: busy, style: { border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 16 } },
|
|
520
|
+
h('h3', null, 'OpenCode Go'),
|
|
504
521
|
h('p', { style: { whiteSpace: 'pre-wrap' } }, state.opencode.lines.join('\n')),
|
|
505
522
|
h('p', null, 'Sign in opens the OpenCode consent page in your browser. It identifies the OpenCode CLI client used for this login.'),
|
|
506
523
|
h('div', { style: { display: 'flex', gap: 8 } },
|
|
@@ -508,15 +525,21 @@ components.push({
|
|
|
508
525
|
state.opencode.pending && button('Cancel OpenCode login', () => run('cancel-opencode')),
|
|
509
526
|
state.opencode.configured && destructive('logout-opencode', 'Sign out of OpenCode Go'))));
|
|
510
527
|
}
|
|
511
|
-
return {
|
|
512
|
-
|
|
528
|
+
return {
|
|
529
|
+
inject: ['slots', 'connection'],
|
|
530
|
+
settingsPage(ctx) { return { entry: { name: 'settings.section', id: 'dscode-accounts', order: 12, label: () => 'Accounts',
|
|
513
531
|
inject: () => ({ execute: async (action, args = {}) => {
|
|
514
532
|
const response = await ctx.connection.rpc.call('/api', 'dscode-accounts', { action, ...args });
|
|
515
533
|
if (!response.ok) throw Error(response.error.message);
|
|
516
534
|
return response.value;
|
|
517
535
|
} }),
|
|
518
|
-
}, Accounts
|
|
519
|
-
|
|
536
|
+
}, Component: Accounts }; },
|
|
537
|
+
apply(ctx, options = {}) {
|
|
538
|
+
if (options.settings === false) return;
|
|
539
|
+
const page = this.settingsPage(ctx);
|
|
540
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register(page.entry, page.Component));
|
|
541
|
+
},
|
|
542
|
+
};
|
|
520
543
|
},
|
|
521
544
|
});
|
|
522
545
|
|
|
@@ -524,7 +547,7 @@ components.push({
|
|
|
524
547
|
id: '@toddzheng024/dscode-desktop',
|
|
525
548
|
factory: require => {
|
|
526
549
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
527
|
-
const control = {
|
|
550
|
+
const control = { boxSizing: 'border-box', minWidth: 0, maxWidth: '100%' };
|
|
528
551
|
const row = { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' };
|
|
529
552
|
const initial = () => ({ id: '', prompt: '', objective: '', kind: 'interval', seconds: '3600', cron: '0 9 * * 1-5', timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
530
553
|
command: '[]', scriptMode: 'poll', persistent: true, permission: 'read-only', enabled: true });
|
|
@@ -613,8 +636,8 @@ components.push({
|
|
|
613
636
|
// the user runs another task or changes workspace before retrying.
|
|
614
637
|
await refresh(); eventKeys.current.delete(identity);
|
|
615
638
|
});
|
|
616
|
-
return h('section', { 'aria-label': 'DSCODE scheduling settings', style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 16, padding:
|
|
617
|
-
h('h2', { style: { margin: 0 } }, '
|
|
639
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE scheduling settings', style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 16, padding: 0, maxWidth: 960, minWidth: 0, boxSizing: 'border-box', overflowWrap: 'anywhere' } },
|
|
640
|
+
h('h2', { style: { margin: 0 } }, 'Schedules'),
|
|
618
641
|
h('p', null, 'Enabling delivery runs due tasks for every registered project and remembers this choice when Desktop opens again. Keep the application open. Stopping delivery interrupts active tasks and script sources; pending jobs stay queued.'),
|
|
619
642
|
error && h('div', { role: 'alert', tabIndex: -1, ref: actionError }, error),
|
|
620
643
|
refreshError && h('div', { role: 'alert' }, refreshError),
|
|
@@ -644,8 +667,8 @@ components.push({
|
|
|
644
667
|
button(`Read ${item.id} source log`, () => run(async () => { const result = await execute('source', { sessionId: selected.current, args: { action: 'logs', trigger_id: item.id } }); if (alive.current) setLog(result.log || 'No source output recorded.'); }))))),
|
|
645
668
|
log && h('pre', { 'aria-label': 'Source log', style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, log),
|
|
646
669
|
!workspace.definitions.length && h('p', null, 'No tasks in this workspace.'),
|
|
647
|
-
h('fieldset', { disabled: busy || !workspace.writable, style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', minWidth: 0, gap: 12, border: '
|
|
648
|
-
h('
|
|
670
|
+
h('fieldset', { className: 'dscode-card', disabled: busy || !workspace.writable, style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', minWidth: 0, gap: 12, border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 16 } },
|
|
671
|
+
h('h3', null, editing ? `Edit ${editing.id}` : 'Create task'), field('Task ID', 'id'),
|
|
649
672
|
h('label', null, 'Task instructions', h('textarea', { 'aria-label': 'Task instructions', style: { ...control, display: 'block', width: '100%', minHeight: 90 }, value: draft.prompt,
|
|
650
673
|
onChange: event => setDraft(previous => ({ ...previous, prompt: event.target.value })) })),
|
|
651
674
|
field('Goal (defaults to task instructions)', 'objective'),
|
|
@@ -664,15 +687,21 @@ components.push({
|
|
|
664
687
|
job.state === 'pending' && button(`Cancel ${job.triggerId} job`, () => mutate('jobs', { action: 'cancel', job_id: job.id }), !workspace.writable))),
|
|
665
688
|
!workspace.jobs.length && h('p', null, 'No jobs in this workspace.')));
|
|
666
689
|
}
|
|
667
|
-
return {
|
|
668
|
-
|
|
690
|
+
return {
|
|
691
|
+
inject: ['slots', 'connection'],
|
|
692
|
+
settingsPage(ctx) { return { entry: { name: 'settings.section', id: 'dscode-schedules', order: 13, label: () => 'Schedules',
|
|
669
693
|
inject: () => ({ execute: async (action, args = {}) => {
|
|
670
694
|
const response = await ctx.connection.rpc.call('/api', 'dscode-triggers', { action, ...args });
|
|
671
695
|
if (!response.ok) throw Error(response.error.message);
|
|
672
696
|
return response.value;
|
|
673
697
|
} }),
|
|
674
|
-
}, Schedules
|
|
675
|
-
|
|
698
|
+
}, Component: Schedules }; },
|
|
699
|
+
apply(ctx, options = {}) {
|
|
700
|
+
if (options.settings === false) return;
|
|
701
|
+
const page = this.settingsPage(ctx);
|
|
702
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register(page.entry, page.Component));
|
|
703
|
+
},
|
|
704
|
+
};
|
|
676
705
|
},
|
|
677
706
|
});
|
|
678
707
|
|
|
@@ -680,7 +709,7 @@ components.push({
|
|
|
680
709
|
id: '@toddzheng024/dscode-desktop',
|
|
681
710
|
factory: require => {
|
|
682
711
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
683
|
-
const control = { padding: '7px 10px', border: '
|
|
712
|
+
const control = { padding: '7px 10px', border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)', background: 'transparent', color: 'inherit', font: 'inherit' };
|
|
684
713
|
function Inbox({ execute }) {
|
|
685
714
|
const [state, setState] = useState(null), [mail, setMail] = useState(null), [offset, setOffset] = useState(0);
|
|
686
715
|
const [error, setError] = useState(''), [notice, setNotice] = useState(''), [busy, setBusy] = useState(false), [sent, setSent] = useState(false);
|
|
@@ -727,7 +756,7 @@ components.push({
|
|
|
727
756
|
const value = await execute('read', { key });
|
|
728
757
|
if (alive.current) { setMail(value); requestId.current = crypto.randomUUID(); }
|
|
729
758
|
});
|
|
730
|
-
return h('section', { 'aria-label': 'DSCODE email inbox', style: { padding: 16, overflow: 'auto', height: '100%', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 12 } },
|
|
759
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE email inbox', style: { padding: 16, overflow: 'auto', height: '100%', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 12 } },
|
|
731
760
|
h('strong', null, 'Email inbox'),
|
|
732
761
|
h('p', { style: { margin: 0, fontSize: 12 } }, 'Shared local inbox. Reading mail does not start the agent. Add only the message you want this session to use as external context.'),
|
|
733
762
|
error && h('div', { role: 'alert' }, error),
|
|
@@ -748,12 +777,12 @@ components.push({
|
|
|
748
777
|
try { await execute('connect-imap', { config: { ...form, port: Number(form.port) } }); }
|
|
749
778
|
finally { if (alive.current) setForm(old => ({ ...old, password: '' })); }
|
|
750
779
|
if (alive.current) await refresh(offset);
|
|
751
|
-
}); } }, h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '
|
|
780
|
+
}); } }, h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)' } },
|
|
752
781
|
h('legend', null, 'IMAP with an application password'),
|
|
753
782
|
field('account', 'Mailbox login'), field('host', 'IMAP host'), field('port', 'TLS port'), field('mailbox', 'Mailbox folder'), field('password', 'Application password', 'password'),
|
|
754
783
|
h('p', { style: { margin: 0, fontSize: 12 } }, 'Uses verified TLS and reads the selected folder without changing flags. The password is saved locally after a successful connection and never enters the conversation. Reconnect the same account and folder to update it.'),
|
|
755
784
|
h('button', { style: control, type: 'submit', disabled: !form.account.trim() || !form.password }, 'Connect IMAP'))),
|
|
756
|
-
h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '
|
|
785
|
+
h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)' } },
|
|
757
786
|
h('legend', null, 'Optional Gmail OAuth'),
|
|
758
787
|
h('label', null, 'Google Desktop client JSON path', h('input', { 'aria-label': 'Google Desktop client JSON path', value: path,
|
|
759
788
|
onChange: event => setPath(event.target.value), style: { ...control, width: '100%', boxSizing: 'border-box' } })),
|
|
@@ -761,7 +790,7 @@ components.push({
|
|
|
761
790
|
h('p', { style: { margin: 0, fontSize: 12 } }, 'Authorization opens Google in your browser and requests mail read access plus label and filter management. It creates or reuses the ToAgent label and subject filter. Use your Google account settings to revoke access.'),
|
|
762
791
|
button('Authorize Gmail', () => action('connect-gmail'), !state?.gmail.configured)))),
|
|
763
792
|
h('div', { 'aria-label': 'Received emails', style: { display: 'grid', gap: 6, maxHeight: '26vh', overflow: 'auto' } },
|
|
764
|
-
...(state?.emails ?? []).map(row => h('button', { key: row.key, type: 'button', style: { ...control, textAlign: 'left', overflowWrap: 'anywhere', borderColor: mail?.key === row.key ? '
|
|
793
|
+
...(state?.emails ?? []).map(row => h('button', { key: row.key, type: 'button', style: { ...control, textAlign: 'left', overflowWrap: 'anywhere', borderColor: mail?.key === row.key ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-border-l3)' },
|
|
765
794
|
disabled: busy, onClick: () => select(row.key) }, h('strong', null, row.subject), h('div', { style: { fontSize: 12 } }, `${row.from} · ${new Date(row.updatedAt).toLocaleString()}`))),
|
|
766
795
|
state?.emails.length === 0 && h('p', null, 'No emails received.')),
|
|
767
796
|
state && h('div', { style: { display: 'flex', gap: 8 } },
|
|
@@ -801,7 +830,7 @@ components.push({
|
|
|
801
830
|
const money = (cost, partial = false) => cost === null || partial && cost === 0 ? 'Unknown' : `$${cost.toFixed(4)}${partial ? '+' : ''}`;
|
|
802
831
|
const percent = value => value === null ? 'Unknown' : `${value.toFixed(1)}%`;
|
|
803
832
|
const speed = value => value === null ? 'Unknown' : `${value.toFixed(1)} tokens/s`;
|
|
804
|
-
const control = { padding: '7px 10px', border: '
|
|
833
|
+
const control = { padding: '7px 10px', border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)', background: 'transparent', color: 'inherit', font: 'inherit' };
|
|
805
834
|
function Usage({ execute }) {
|
|
806
835
|
const [state, setState] = useState(null), [error, setError] = useState('');
|
|
807
836
|
const active = useRef(false), alive = useRef(true);
|
|
@@ -813,9 +842,9 @@ components.push({
|
|
|
813
842
|
};
|
|
814
843
|
useEffect(() => { alive.current = true; void refresh(); const timer = setInterval(refresh, 1000);
|
|
815
844
|
return () => { alive.current = false; clearInterval(timer); }; }, []);
|
|
816
|
-
const fact = (label, value) => h('div', { key: label, style: { padding: 10, border: '
|
|
845
|
+
const fact = (label, value) => h('div', { key: label, style: { padding: 10, border: '.5px solid var(--dsw-alias-border-l2, #8884)', borderRadius: 'var(--dsw-radius-md, 6px)' } },
|
|
817
846
|
h('dt', { style: { fontSize: 12, marginBottom: 5 } }, label), h('dd', { style: { margin: 0, fontVariantNumeric: 'tabular-nums' } }, value));
|
|
818
|
-
return h('section', { 'aria-label': 'DSCODE session usage', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
|
|
847
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE session usage', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
|
|
819
848
|
h('h2', { style: { marginTop: 0 } }, 'Session usage'),
|
|
820
849
|
h('p', { style: { fontSize: 12 } }, 'Recorded model usage for this DSCODE session, including attributed child and background calls. Costs are estimates, not an invoice.'),
|
|
821
850
|
h('button', { type: 'button', style: control, onClick: refresh }, 'Refresh usage'),
|
|
@@ -862,7 +891,7 @@ components.push({
|
|
|
862
891
|
factory: require => {
|
|
863
892
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
864
893
|
const columns = [['pending', 'Pending'], ['running', 'Running'], ['verifying', 'Verifying'], ['complete', 'Complete']];
|
|
865
|
-
const control = { padding: '7px 10px', border: '
|
|
894
|
+
const control = { padding: '7px 10px', border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)', background: 'transparent', color: 'inherit', font: 'inherit' };
|
|
866
895
|
function Board({ execute }) {
|
|
867
896
|
const [state, setState] = useState(null), [error, setError] = useState('');
|
|
868
897
|
const active = useRef(false), alive = useRef(true);
|
|
@@ -874,7 +903,7 @@ components.push({
|
|
|
874
903
|
};
|
|
875
904
|
useEffect(() => { alive.current = true; void refresh(); const timer = setInterval(refresh, 1000);
|
|
876
905
|
return () => { alive.current = false; clearInterval(timer); }; }, []);
|
|
877
|
-
const card = (task, column) => h('li', { key: task.id, style: { listStyle: 'none', border: '
|
|
906
|
+
const card = (task, column) => h('li', { key: task.id, style: { listStyle: 'none', border: '.5px solid var(--dsw-alias-settings-card-stroke, #8885)', borderRadius: 'var(--dsw-radius-md, 6px)', padding: 10, marginTop: 8, overflowWrap: 'anywhere' } },
|
|
878
907
|
h('strong', null, `${task.id} · ${task.title}`),
|
|
879
908
|
h('p', { style: { margin: '6px 0', fontSize: 12 } }, `/${task.child} · ${task.priority} priority`),
|
|
880
909
|
task.waiting && h('p', null, 'Waiting for the coordinator’s answer'),
|
|
@@ -884,7 +913,7 @@ components.push({
|
|
|
884
913
|
(task.detail || task.note || task.verification || task.worktree) && h('details', null, h('summary', null, 'Task details'),
|
|
885
914
|
...[['Scope and checks', task.detail], ['Reopened because', task.note], ['Verification evidence', task.verification], ['Worktree', task.worktree]]
|
|
886
915
|
.filter(([, value]) => value).map(([label, value]) => h('div', { key: label, style: { marginTop: 8 } }, h('strong', null, label), h('p', { style: { whiteSpace: 'pre-wrap', margin: '4px 0' } }, value)))));
|
|
887
|
-
return h('section', { 'aria-label': 'DSCODE delegation board', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
|
|
916
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE delegation board', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
|
|
888
917
|
h('h2', { style: { marginTop: 0 } }, 'Delegation board'),
|
|
889
918
|
h('p', { style: { fontSize: 12 } }, 'Use /delegate <task> in the main session to coordinate work in isolated Git worktrees. The coordinator plans tasks and records verified results.'),
|
|
890
919
|
h('button', { type: 'button', style: control, onClick: refresh }, 'Refresh board'),
|
|
@@ -916,114 +945,50 @@ components.push({
|
|
|
916
945
|
components.push({
|
|
917
946
|
id: '@toddzheng024/dscode-desktop',
|
|
918
947
|
factory: require => {
|
|
919
|
-
const { createElement: h, useState
|
|
920
|
-
const
|
|
921
|
-
const
|
|
922
|
-
function
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
const [operation, setOperation] = useState(null), [error, setError] = useState(''), [busy, setBusy] = useState(false);
|
|
926
|
-
const alive = useRef(true), locked = useRef(false), pageQuery = useRef({ query: '', category: '' });
|
|
927
|
-
const run = async fn => {
|
|
928
|
-
if (locked.current) return;
|
|
929
|
-
locked.current = true; setBusy(true); setError('');
|
|
930
|
-
try { await fn(); } catch (error) { if (alive.current) setError(error.message); }
|
|
931
|
-
finally { locked.current = false; if (alive.current) setBusy(false); }
|
|
932
|
-
};
|
|
933
|
-
const search = async (more = false) => {
|
|
934
|
-
const input = more ? pageQuery.current : { query, category };
|
|
935
|
-
if (!more) { setDetail(null); setPreview(null); setResults(null); }
|
|
936
|
-
const value = await execute('search', { ...input, ...(more ? results.nextPage ? { page: results.nextPage } : { cursor: results.nextCursor } : {}) });
|
|
937
|
-
if (!alive.current) return;
|
|
938
|
-
pageQuery.current = input;
|
|
939
|
-
setResults(more ? { ...value, items: [...results.items, ...value.items.filter(item => !results.items.some(previous => previous.packageName === item.packageName))] } : value);
|
|
940
|
-
};
|
|
941
|
-
const load = () => run(async () => {
|
|
942
|
-
const [list, initial, status] = await Promise.all([execute('categories'), execute('search', { query: '', category: '' }), execute('status')]);
|
|
943
|
-
if (alive.current) { setCategories(list.items); setResults(initial); setOperation(status.operation); pageQuery.current = { query: '', category: '' }; }
|
|
944
|
-
});
|
|
945
|
-
useEffect(() => { alive.current = true; void load(); return () => { alive.current = false; }; }, []);
|
|
946
|
-
useEffect(() => {
|
|
947
|
-
if (operation?.status !== 'running') return;
|
|
948
|
-
let stopped = false, polling = false;
|
|
949
|
-
const timer = setInterval(async () => {
|
|
950
|
-
if (polling) return; polling = true;
|
|
951
|
-
try {
|
|
952
|
-
const value = await execute('operation', { requestId: operation.requestId });
|
|
953
|
-
if (!stopped && alive.current) setOperation(value);
|
|
954
|
-
} catch (error) { if (!stopped && alive.current) setError(`Could not refresh installation: ${error.message}`); }
|
|
955
|
-
finally { polling = false; }
|
|
956
|
-
}, 1000);
|
|
957
|
-
return () => { stopped = true; clearInterval(timer); };
|
|
958
|
-
}, [operation?.requestId, operation?.status]);
|
|
959
|
-
const installing = operation?.status === 'running';
|
|
960
|
-
const button = (label, action, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: action }, label);
|
|
961
|
-
const openManagement = packageName => run(async () => { manage(packageName); close?.(); });
|
|
962
|
-
const show = name => run(async () => {
|
|
963
|
-
setDetail(null); setPreview(null);
|
|
964
|
-
const value = await execute('detail', { packageName: name }); if (alive.current) setDetail(value);
|
|
965
|
-
});
|
|
966
|
-
const installed = detail?.installed || (operation?.packageName === detail?.packageName && operation?.result?.application === 'applied' && operation.result.bundle === detail?.packageName);
|
|
967
|
-
const security = item => `Hub scan: ${item.security?.status ?? 'not reported'}`;
|
|
968
|
-
const result = operation?.result;
|
|
969
|
-
return h('section', { 'aria-label': 'Plugin Hub', style: { padding: 20, display: 'grid', gap: 16, maxWidth: 900 } },
|
|
970
|
-
h('h2', { style: { margin: 0 } }, 'Plugin Hub'),
|
|
971
|
-
h('p', null, 'Discover community plugins from DSH Plugin Hub. Install here, then enable and configure in Settings → Plugins.'),
|
|
972
|
-
h('form', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 }, onSubmit: event => { event.preventDefault(); void run(() => search()); } },
|
|
973
|
-
h('input', { type: 'search', 'aria-label': 'Search plugins', placeholder: 'Search plugins or describe a capability', value: query, maxLength: 300, style: { ...control, flex: 1 }, disabled: busy || installing, onChange: event => setQuery(event.target.value) }),
|
|
974
|
-
h('select', { 'aria-label': 'Plugin category', value: category, style: control, disabled: busy || installing, onChange: event => setCategory(event.target.value) },
|
|
975
|
-
h('option', { value: '' }, 'All categories'), ...categories.map(item => h('option', { key: item.name, value: item.name }, `${item.displayName} (${item.count})`))),
|
|
976
|
-
h('button', { type: 'submit', disabled: busy || installing, style: control }, busy ? 'Loading…' : 'Search')),
|
|
977
|
-
error && h('div', { role: 'alert' }, error, ' ', button('Refresh catalog', load, installing)),
|
|
978
|
-
operation && h('div', { role: 'status', style: box },
|
|
979
|
-
h('strong', null, operation.spec),
|
|
980
|
-
h('p', null, installing ? 'Installing with the official plugin manager…' : operation.error ? operation.error : result?.application === 'applied' ? 'Installation completed. Enable the plugin in Settings → Plugins.' :
|
|
981
|
-
result?.application === 'restart-required' ? 'The plugin manager requires a Desktop restart. Check Settings → Plugins after restarting.' :
|
|
982
|
-
result?.application === 'cancelled' ? 'Installation cancelled.' : `Installation outcome: ${result?.application ?? 'unknown'}. Check Settings → Plugins.`),
|
|
983
|
-
result?.error && h('p', null, result.error.message),
|
|
984
|
-
result?.warnings?.map((warning, index) => h('p', { key: index }, warning)),
|
|
985
|
-
result?.pendingBuilds?.length > 0 && h('p', null, `Build approval is required for ${result.pendingBuilds.join(', ')}. Review it through the official plugin manager.`),
|
|
986
|
-
installing ? button('Cancel installation', () => run(async () => { const value = await execute('cancel', { requestId: operation.requestId }); if (alive.current) setOperation(value); })) :
|
|
987
|
-
button('Manage installed plugin', () => openManagement(operation.packageName))),
|
|
988
|
-
detail && h('article', { style: box, 'aria-label': 'Plugin details' },
|
|
989
|
-
h('h3', null, detail.displayName), h('code', null, `${detail.packageName}@${detail.latestVersion}`),
|
|
990
|
-
h('p', null, detail.summary), h('p', { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, detail.description),
|
|
991
|
-
h('p', null, `${detail.categories.join(' · ')} · ${detail.weeklyDownloads} weekly downloads`),
|
|
992
|
-
h('p', null, `Repository: ${detail.repository} · License: ${detail.license ?? 'not reported'}`),
|
|
993
|
-
h('p', null, `${detail.claimed ? 'Publisher claimed' : 'Publisher unclaimed'} · ${detail.verified ? 'Publisher verified' : 'Publisher unverified'}`),
|
|
994
|
-
h('p', null, security(detail), '. Publisher verification and scans do not guarantee safety.'),
|
|
995
|
-
detail.security && h('ul', null, ...[['dependencyInventoryComplete', 'Complete dependency inventory'], ['advisoryScanned', 'Vulnerability advisories checked'], ['behaviorAnalyzed', 'Behavior analysis completed']].map(([key, label]) => h('li', { key }, `${label}: ${detail.security[key] === true ? 'yes' : 'no'}`))),
|
|
996
|
-
h('a', { href: detail.url, target: '_blank', rel: 'noreferrer' }, 'View on DSH Plugin Hub'),
|
|
997
|
-
h('p', null, `Your Desktop: DSH ${detail.environment.runtime} · ${detail.environment.platform}`),
|
|
998
|
-
detail.candidate.compatibility && h('p', null, `Declared compatibility: DSH ${detail.candidate.compatibility.dsh} · ${detail.candidate.compatibility.surfaces.join(', ')} · ${detail.candidate.compatibility.platforms.join(', ') || 'all platforms'}`),
|
|
999
|
-
detail.candidate.reasons.length > 0 && h('ul', null, ...detail.candidate.reasons.map(reason => h('li', { key: reason }, reason))),
|
|
1000
|
-
installed ? button('Manage in Plugins', () => openManagement(detail.packageName), installing) :
|
|
1001
|
-
button('Review installation', () => run(async () => { const value = await execute('prepare', { packageName: detail.packageName }); if (alive.current) { setDetail(value.detail); setPreview(value); } }), installing || detail.candidate.reasons.length > 0),
|
|
1002
|
-
preview && h('div', { style: { ...box, marginTop: 16 }, role: 'group', 'aria-label': 'Confirm plugin installation' },
|
|
1003
|
-
h('p', null, `Install ${preview.detail.candidate.spec} from ${preview.inspection.registry ?? 'your configured npm registry'}?`),
|
|
1004
|
-
h('p', null, 'The official manager will download the package and its dependencies. It will remain disabled until you enable it in Plugins. Enabling third-party plugins runs their code in the Host.'),
|
|
1005
|
-
button('Confirm installation', () => run(async () => { const value = await execute('install', { token: preview.token, confirm: true }); if (alive.current) { setOperation(value); setPreview(null); } }), installing),
|
|
1006
|
-
' ', button('Back', () => setPreview(null), installing))),
|
|
1007
|
-
results && h('div', { 'aria-label': 'Plugin search results', style: { display: 'grid', gap: 12 } },
|
|
1008
|
-
results.items.length === 0 && h('p', null, 'No plugins match this search. Try another phrase or category.'),
|
|
1009
|
-
...results.items.map(item => h('article', { key: item.packageName, style: box },
|
|
1010
|
-
h('h3', { style: { margin: 0 } }, item.displayName), h('code', null, `${item.packageName}@${item.latestVersion}`),
|
|
1011
|
-
h('p', null, item.summary), h('p', null, `${security(item)}${item.deprecated ? ' · Deprecated' : ''}`),
|
|
1012
|
-
button('View details', () => show(item.packageName), installing))),
|
|
1013
|
-
(results.nextPage || results.nextCursor) && button('Load more plugins', () => run(() => search(true)), installing)));
|
|
948
|
+
const { createElement: h, useState } = require('react');
|
|
949
|
+
const packageName = '@toddzheng024/dscode-desktop';
|
|
950
|
+
const labels = { 'dscode-models': 'Models', 'dscode-accounts': 'Accounts', 'dscode-schedules': 'Schedules' };
|
|
951
|
+
function Mark() {
|
|
952
|
+
return h('svg', { width: 28, height: 28, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.7, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true },
|
|
953
|
+
h('rect', { x: 2, y: 4, width: 20, height: 16, rx: 4 }), h('path', { d: 'm7 9 3 3-3 3m6 0h4' }));
|
|
1014
954
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
955
|
+
function Setup() {
|
|
956
|
+
const steps = [
|
|
957
|
+
['Enable and restart', 'Enable the DSCODE bundle in Plugins. After installing or updating, fully quit and reopen DeepSeek Harness. Closing a window is not enough.'],
|
|
958
|
+
['Show agent presets', 'In Settings → General, enable Show coding view. If Agent presets is disabled in Plugins, enable it too.'],
|
|
959
|
+
['Start a DSCODE session', 'Create a new session and choose DSCODE in the Agent preset picker. Existing sessions keep their original preset.'],
|
|
960
|
+
['Use your existing account', 'Choose a model in the normal model picker. No second login or copied API key is required for models you already use.'],
|
|
961
|
+
];
|
|
962
|
+
return h('section', { className: 'dscode-setup', 'aria-label': 'Start using DSCODE' },
|
|
963
|
+
h('h3', null, 'Start using DSCODE'),
|
|
964
|
+
h('ol', { className: 'dscode-steps' }, ...steps.map(([title, description]) =>
|
|
965
|
+
h('li', { key: title }, h('div', null, h('strong', null, title), h('p', null, description))))),
|
|
966
|
+
h('p', { className: 'dscode-note' }, 'Browser preview controls DSCODE’s external Chrome. The official Browser remains available for ordinary browsing.'));
|
|
967
|
+
}
|
|
968
|
+
function Settings({ pages, view }) {
|
|
969
|
+
const [selected, setSelected] = useState('overview');
|
|
970
|
+
if (view === 'summary') return h('span', null, 'Community DSCODE coding preset · Setup, additional providers and schedules');
|
|
971
|
+
const page = pages.find(page => page.entry.id === selected);
|
|
972
|
+
return h('section', { 'aria-label': 'DSCODE settings', className: 'dscode-ui dscode-settings' },
|
|
973
|
+
view !== 'page' && h('div', { className: 'dscode-heading' }, h(Mark), h('h2', null, 'DSCODE'), h('span', null, 'Community plugin')),
|
|
974
|
+
h('p', null, 'Coding workflows in Harness Desktop. Your existing models and accounts work here.'),
|
|
975
|
+
h('nav', { 'aria-label': 'DSCODE settings pages', className: 'dscode-tabs' },
|
|
976
|
+
...[['overview', 'Get started'], ...pages.map(page => [page.entry.id, labels[page.entry.id]])].map(([id, label]) =>
|
|
977
|
+
h('button', { key: id, type: 'button', 'aria-pressed': selected === id, onClick: () => setSelected(id) }, label))),
|
|
978
|
+
page ? h(page.Component, { key: selected, ...page.entry.inject() }) : h(Setup));
|
|
979
|
+
}
|
|
980
|
+
function Activation({ onDismiss, onOpenDetails }) {
|
|
981
|
+
return h('section', { className: 'dscode-ui dscode-activation' }, h('h2', null, 'DSCODE enabled — restart to finish setup'), h(Setup),
|
|
982
|
+
h('div', { className: 'dscode-actions' },
|
|
983
|
+
h('button', { type: 'button', className: 'dscode-primary', onClick: onOpenDetails }, 'Open DSCODE setup'),
|
|
984
|
+
h('button', { type: 'button', onClick: onDismiss }, 'Close')));
|
|
985
|
+
}
|
|
986
|
+
return { inject: ['slots'], apply(ctx, { pages = [] } = {}) {
|
|
987
|
+
const props = () => ({ pages });
|
|
988
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode', order: 11, label: () => 'DSCODE', inject: props }, Settings));
|
|
989
|
+
ctx.slots.inject('plugins.item', () => ctx.slots.register({ name: 'plugins.item', id: 'dscode', order: 90, label: () => 'DSCODE · Community', inject: props }, Settings));
|
|
990
|
+
ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: packageName, inject: props }, Settings));
|
|
991
|
+
ctx.slots.inject('plugins.bundle.activation', () => ctx.slots.register({ name: 'plugins.bundle.activation', key: packageName }, Activation));
|
|
1027
992
|
} };
|
|
1028
993
|
},
|
|
1029
994
|
});
|
|
@@ -1031,10 +996,19 @@ components.push({
|
|
|
1031
996
|
globalThis.__ModuleLoader__.load({
|
|
1032
997
|
id: "@toddzheng024/dscode-desktop",
|
|
1033
998
|
factory: require => {
|
|
999
|
+
if (typeof document !== 'undefined' && !document.querySelector('style[data-dscode-ui]')) {
|
|
1000
|
+
const style = document.createElement('style');
|
|
1001
|
+
style.dataset.dscodeUi = '';
|
|
1002
|
+
style.textContent = "/* Scoped to DSCODE. Tokens are supplied by Harness ui-theme (0.2.0-rc.2).\n Do not target the host's hashed classes or change other plugins' controls. */\n.dscode-ui { box-sizing: border-box; color: var(--dsw-alias-label-primary); font-family: inherit; font-size: 14px; line-height: 22px; min-width: 0; }\n.dscode-ui *, .dscode-ui *::before, .dscode-ui *::after { box-sizing: border-box; }\n.dscode-ui h2 { font-size: 16px; line-height: 24px; font-weight: 500; margin: 0; }\n.dscode-ui h3, .dscode-ui legend { font-size: 14px; line-height: 22px; font-weight: 500; margin: 0; }\n.dscode-ui p { margin: 0; color: var(--dsw-alias-label-tertiary); }\n.dscode-ui button { cursor: pointer; min-height: 36px; padding: 6px 14px; border: .5px solid var(--dsw-alias-border-l3); border-radius: var(--dsw-radius-md); color: var(--dsw-alias-label-primary); background: transparent; font: inherit; }\n.dscode-ui button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n.dscode-ui button:disabled { opacity: .4; cursor: default; }\n.dscode-ui :is(button, input, select, textarea):focus-visible { outline: 2px solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: 2px; }\n.dscode-ui :is(input:not([type=checkbox]), select, textarea) { min-width: 0; max-width: 100%; min-height: 36px; padding: 6px 10px; border: .5px solid var(--dsw-alias-border-l3); border-radius: var(--dsw-radius-md); color: var(--dsw-alias-label-primary); background: var(--dsw-alias-settings-card-fill); font: inherit; }\n.dscode-ui input[type=checkbox] { accent-color: var(--dsw-alias-state-business-primary); margin-right: 8px; }\n.dscode-ui label { color: var(--dsw-alias-label-secondary); font-size: 13px; }\n.dscode-ui fieldset { min-width: 0; }\n.dscode-ui [role=alert] { color: var(--dsw-alias-state-error-primary); overflow-wrap: anywhere; }\n.dscode-ui [role=status] { font-size: 12px; color: var(--dsw-alias-label-secondary); }\n.dscode-settings { padding: 8px 0; display: grid; gap: 16px; }\n.dscode-heading { display: flex; align-items: center; gap: 8px; }\n.dscode-heading svg { flex: none; width: 20px; height: 20px; }\n.dscode-heading span { margin-left: auto; color: var(--dsw-alias-label-tertiary); font-size: 12px; }\n.dscode-tabs { display: flex; flex-wrap: wrap; gap: 4px; padding-bottom: 12px; border-bottom: .5px solid var(--dsw-alias-border-l2); }\n.dscode-tabs button { border: 0; padding: 6px 12px; }\n.dscode-tabs button[aria-pressed=true] { background: var(--dsw-specific-sidebar-nav-item-active); }\n.dscode-setup { display: grid; gap: 12px; }\n.dscode-steps { list-style: none; margin: 0; padding: 0; counter-reset: setup; }\n.dscode-steps li { counter-increment: setup; display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 12px; padding: 16px 0; border-bottom: .5px solid var(--dsw-alias-border-l2); }\n.dscode-steps li::before { content: counter(setup); display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: var(--dsw-alias-bg-module-platform); color: var(--dsw-alias-label-secondary); font-size: 12px; }\n.dscode-steps strong { display: block; font-weight: 500; margin-bottom: 4px; }\n.dscode-steps p, .dscode-note { font-size: 13px; line-height: 20px; }\n.dscode-activation { display: grid; gap: 16px; padding: 16px 0; }\n.dscode-actions { display: flex; flex-wrap: wrap; gap: 8px; }\n.dscode-ui .dscode-primary { background: var(--dsw-alias-button-primary-fill); color: var(--dsw-alias-label-primary-foreground); border-color: transparent; }\n.dscode-ui .dscode-primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }\n\n.dscode-ui .dscode-card { background: var(--dsw-alias-settings-card-fill); margin: 0; display: grid; gap: 12px; }\n";
|
|
1003
|
+
document.head.appendChild(style);
|
|
1004
|
+
}
|
|
1034
1005
|
const plugins = components.map(component => component.factory(require));
|
|
1035
1006
|
return {
|
|
1036
1007
|
inject: [...new Set(plugins.flatMap(plugin => plugin.inject ?? []))],
|
|
1037
|
-
apply(ctx) {
|
|
1008
|
+
apply(ctx) {
|
|
1009
|
+
const pages = plugins.flatMap(plugin => plugin.settingsPage ? [plugin.settingsPage(ctx)] : []);
|
|
1010
|
+
for (const plugin of plugins) plugin.apply(ctx, { settings: false, pages, presetOnly: true });
|
|
1011
|
+
},
|
|
1038
1012
|
};
|
|
1039
1013
|
},
|
|
1040
1014
|
});
|