@banana-peeljj12/dsh-trellis 0.3.1 → 0.3.2
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/lib/client.js +94 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -27,6 +27,14 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
|
|
28
28
|
let react = require('react');
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Module-level client root context, captured in apply(ctx). Components (slot
|
|
32
|
+
* registrations) do not receive ctx directly, but need host services such as
|
|
33
|
+
* `sidebarRight` (native document preview) at click time — this is the
|
|
34
|
+
* bridge. Null until apply runs, which happens before any component mounts.
|
|
35
|
+
*/
|
|
36
|
+
let clientCtx = null;
|
|
37
|
+
|
|
30
38
|
// #region lib/types/client/trellis-settings-tab.js
|
|
31
39
|
|
|
32
40
|
/** Settings namespace registered by the Host half (see lib/meta.js). */
|
|
@@ -36,7 +44,7 @@ window.__ModuleLoader__.load({
|
|
|
36
44
|
const LOCALE_NS = 'settings.trellisWorkflow';
|
|
37
45
|
|
|
38
46
|
/** Services required by this client half. */
|
|
39
|
-
const inject = ['slots', 'locale', 'settingsScope'];
|
|
47
|
+
const inject = ['slots', 'locale', 'settingsScope', 'sidebarRight'];
|
|
40
48
|
|
|
41
49
|
const zh = {
|
|
42
50
|
tab: 'Trellis 工作流',
|
|
@@ -94,6 +102,7 @@ window.__ModuleLoader__.load({
|
|
|
94
102
|
sendToChat: '推进任务',
|
|
95
103
|
sendToChatSuccess: '已填入输入框,回车即可发送',
|
|
96
104
|
sendToChatFailed: '自动填入失败,已复制到剪贴板',
|
|
105
|
+
openFailed: '打开失败,文件引用已复制到剪贴板',
|
|
97
106
|
pendingVerification: '待验证',
|
|
98
107
|
blocked: '阻塞',
|
|
99
108
|
open: '打开',
|
|
@@ -171,6 +180,7 @@ window.__ModuleLoader__.load({
|
|
|
171
180
|
sendToChat: 'Push to chat',
|
|
172
181
|
sendToChatSuccess: 'Filled into the input — press Enter to send',
|
|
173
182
|
sendToChatFailed: 'Auto-fill failed, copied to clipboard',
|
|
183
|
+
openFailed: 'Failed to open — file reference copied to clipboard',
|
|
174
184
|
pendingVerification: 'Verifying',
|
|
175
185
|
blocked: 'Blocked',
|
|
176
186
|
open: 'Open',
|
|
@@ -942,8 +952,72 @@ window.__ModuleLoader__.load({
|
|
|
942
952
|
return '@.trellis/tasks/' + task.slug + '/' + name;
|
|
943
953
|
}
|
|
944
954
|
|
|
955
|
+
/**
|
|
956
|
+
* DSH-native file address for an artifact token. Mirrors the official
|
|
957
|
+
* `fileAddressFor` / `sessionFileAddress` contract
|
|
958
|
+
* (@deepseek-ai/dsh-client-ui-sidebar-files): the token's path (minus the
|
|
959
|
+
* leading `@`) is a workspace-relative path, so it maps to a session-scoped
|
|
960
|
+
* `dsh-resource://file/session/<sessionId>/<path>` address the right-sidebar
|
|
961
|
+
* document preview resolves against the session's workspace. Path segments
|
|
962
|
+
* are component-encoded, keeping `:` literal for drive letters.
|
|
963
|
+
* @param {string} sessionId the session whose workspace resolves the path.
|
|
964
|
+
* @param {string} token artifact token from {@link artifactToken}.
|
|
965
|
+
* @returns {string} `dsh-resource://file/session/...` address.
|
|
966
|
+
*/
|
|
967
|
+
function artifactResourceUrl(sessionId, token) {
|
|
968
|
+
const path = (token || '').replace(/^@/, '');
|
|
969
|
+
const encodeSegment = (segment) => encodeURIComponent(segment).replace(/%3A/gi, ':');
|
|
970
|
+
const encodedPath = path
|
|
971
|
+
.replace(/\\/g, '/')
|
|
972
|
+
.split('/')
|
|
973
|
+
.map(encodeSegment)
|
|
974
|
+
.join('/');
|
|
975
|
+
return 'dsh-resource://file/session/' + encodeSegment(sessionId) + '/' + encodedPath;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Open an artifact file in the DSH-native document preview (right sidebar),
|
|
980
|
+
* mirroring how the official chat client opens `@file` references
|
|
981
|
+
* (`ctx.sidebarRight.openResource(...)`). Never touches the composer input.
|
|
982
|
+
* Falls back to copying the token to the clipboard when the right-sidebar
|
|
983
|
+
* service is unavailable or the open fails, so the click still yields
|
|
984
|
+
* something useful without polluting the input box.
|
|
985
|
+
* @param {object} ctx client root context (with `sidebarRight` injected).
|
|
986
|
+
* @param {string} sessionId the session owning the artifact's workspace.
|
|
987
|
+
* @param {string} token artifact token from {@link artifactToken}.
|
|
988
|
+
* @returns {boolean} true when opened natively; false when only copied.
|
|
989
|
+
*/
|
|
990
|
+
function openArtifactFile(ctx, sessionId, token) {
|
|
991
|
+
try {
|
|
992
|
+
const sidebar = ctx && ctx.sidebarRight;
|
|
993
|
+
if (sidebar && typeof sidebar.openResource === 'function') {
|
|
994
|
+
sidebar.openResource(artifactResourceUrl(sessionId, token));
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
} catch {
|
|
998
|
+
/* fall through to clipboard copy */
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1002
|
+
navigator.clipboard.writeText(token);
|
|
1003
|
+
} else {
|
|
1004
|
+
const ta = document.createElement('textarea');
|
|
1005
|
+
ta.value = token;
|
|
1006
|
+
ta.style.position = 'fixed';
|
|
1007
|
+
ta.style.opacity = '0';
|
|
1008
|
+
document.body.appendChild(ta);
|
|
1009
|
+
ta.select();
|
|
1010
|
+
document.execCommand('copy');
|
|
1011
|
+
document.body.removeChild(ta);
|
|
1012
|
+
}
|
|
1013
|
+
} catch {
|
|
1014
|
+
/* nothing more we can do */
|
|
1015
|
+
}
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
945
1019
|
function KanbanDetails(props) {
|
|
946
|
-
const { task, t, active, busy, onActivate, onDeactivate, tracks } = props;
|
|
1020
|
+
const { task, t, active, busy, onActivate, onDeactivate, tracks, onOpenArtifact } = props;
|
|
947
1021
|
// One-shot injection notice: 'ok' | 'fail' | null, auto-clears.
|
|
948
1022
|
const [notice, setNotice] = react.useState(null);
|
|
949
1023
|
react.useEffect(() => {
|
|
@@ -1276,7 +1350,11 @@ window.__ModuleLoader__.load({
|
|
|
1276
1350
|
textAlign: 'center',
|
|
1277
1351
|
},
|
|
1278
1352
|
},
|
|
1279
|
-
notice === 'ok'
|
|
1353
|
+
notice === 'ok'
|
|
1354
|
+
? t('sendToChatSuccess')
|
|
1355
|
+
: notice === 'open-fail'
|
|
1356
|
+
? t('openFailed')
|
|
1357
|
+
: t('sendToChatFailed'),
|
|
1280
1358
|
)
|
|
1281
1359
|
: null,
|
|
1282
1360
|
),
|
|
@@ -1336,7 +1414,13 @@ window.__ModuleLoader__.load({
|
|
|
1336
1414
|
'button',
|
|
1337
1415
|
{
|
|
1338
1416
|
type: 'button',
|
|
1339
|
-
onClick: () =>
|
|
1417
|
+
onClick: () => {
|
|
1418
|
+
const token = artifactToken(task, name);
|
|
1419
|
+
const opened = onOpenArtifact ? onOpenArtifact(token) : false;
|
|
1420
|
+
// Notice only on failure: a successful native open is
|
|
1421
|
+
// visible in the right sidebar already, so no toast.
|
|
1422
|
+
setNotice(opened ? null : 'open-fail');
|
|
1423
|
+
},
|
|
1340
1424
|
title: t('open') + ' ' + artifactToken(task, name),
|
|
1341
1425
|
style: {
|
|
1342
1426
|
display: 'inline-flex',
|
|
@@ -1814,6 +1898,7 @@ window.__ModuleLoader__.load({
|
|
|
1814
1898
|
const {
|
|
1815
1899
|
board,
|
|
1816
1900
|
t,
|
|
1901
|
+
sessionId,
|
|
1817
1902
|
selected,
|
|
1818
1903
|
onSelect,
|
|
1819
1904
|
busy,
|
|
@@ -1823,6 +1908,7 @@ window.__ModuleLoader__.load({
|
|
|
1823
1908
|
onFilterChange,
|
|
1824
1909
|
onClose,
|
|
1825
1910
|
initialViewMode,
|
|
1911
|
+
onOpenArtifact,
|
|
1826
1912
|
} = props;
|
|
1827
1913
|
const [query, setQuery] = react.useState('');
|
|
1828
1914
|
const [viewMode, setViewMode] = react.useState(initialViewMode === 'list' ? 'list' : 'lanes'); // 'lanes' | 'list'
|
|
@@ -2424,6 +2510,7 @@ window.__ModuleLoader__.load({
|
|
|
2424
2510
|
onActivate: () => onActivate(selectedTask.slug),
|
|
2425
2511
|
onDeactivate,
|
|
2426
2512
|
tracks,
|
|
2513
|
+
onOpenArtifact: (token) => openArtifactFile(clientCtx, sessionId, token),
|
|
2427
2514
|
}),
|
|
2428
2515
|
),
|
|
2429
2516
|
),
|
|
@@ -2812,6 +2899,7 @@ window.__ModuleLoader__.load({
|
|
|
2812
2899
|
? react.createElement(KanbanExpandedModal, {
|
|
2813
2900
|
board,
|
|
2814
2901
|
t,
|
|
2902
|
+
sessionId,
|
|
2815
2903
|
selected,
|
|
2816
2904
|
onSelect: setSelected,
|
|
2817
2905
|
busy,
|
|
@@ -2820,6 +2908,7 @@ window.__ModuleLoader__.load({
|
|
|
2820
2908
|
filter,
|
|
2821
2909
|
onFilterChange: setFilter,
|
|
2822
2910
|
onClose: closeExpanded,
|
|
2911
|
+
onOpenArtifact: (token) => openArtifactFile(clientCtx, sessionId, token),
|
|
2823
2912
|
})
|
|
2824
2913
|
: null;
|
|
2825
2914
|
|
|
@@ -2877,6 +2966,7 @@ window.__ModuleLoader__.load({
|
|
|
2877
2966
|
// #endregion
|
|
2878
2967
|
|
|
2879
2968
|
function apply(ctx) {
|
|
2969
|
+
clientCtx = ctx;
|
|
2880
2970
|
ctx.effect(() => ctx.locale.register(LOCALE_NS, { zh, en }), 'trellis-workflow: dictionaries');
|
|
2881
2971
|
const t = ctx.locale.bind(LOCALE_NS);
|
|
2882
2972
|
// Bound once on this plugin's fiber: the scope's disposer follows the fiber.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@banana-peeljj12/dsh-trellis",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Self-contained Trellis workflow trigger for DeepSeek Harness: per-turn breadcrumb injection via agent/pre-step, a bundled skill provider, and DSH-native task/phase state resolution. No Trellis AGPL code is vendored; workflow semantics are rewritten and licensed MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|