dsh-folder-sidebar 0.0.0-stage → 0.17.0

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.
@@ -0,0 +1,4 @@
1
+ - insert:
2
+ - id: folder-sidebar
3
+ name: dsh-folder-sidebar
4
+ config: {}
package/icon.svg ADDED
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Folder Sidebar">
2
+ <rect x="4" y="4" width="56" height="56" rx="14" fill="#3f6be0"/>
3
+ <path d="M14 22h13l4 5h19a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H14a3 3 0 0 1-3-3V25a3 3 0 0 1 3-3Z"
4
+ fill="none" stroke="#ffffff" stroke-width="4" stroke-linejoin="round"/>
5
+ <path d="M24 36h16" stroke="#ffffff" stroke-width="3" stroke-linecap="round" opacity="0.85"/>
6
+ </svg>
package/index.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Host half of dsh-folder-sidebar.
3
+ *
4
+ * The sidebar replacement is Client-only except for one fact the Client cannot
5
+ * derive: whether a Session's most recent turn failed or was interrupted. A
6
+ * Session summary carries no failure field, so this projection folds
7
+ * `turn/start` and `turn/end` into a small client-visible state per Session.
8
+ *
9
+ * The Client reads it from the sessions list snapshot
10
+ * (`projectionsBySession[id].values['folder-sidebar/turn']`) — the value is
11
+ * computed here and streamed, never folded in the browser.
12
+ */
13
+
14
+ import { z } from 'zod'
15
+
16
+ export const name = 'folder-sidebar'
17
+ export const inject = ['sessionProjections']
18
+
19
+ /** Must match the key the Client reads. */
20
+ const KEY = 'folder-sidebar/turn'
21
+
22
+ /** Bump when the folded state or its meaning changes; stale checkpoints drop. */
23
+ const STATE_VERSION = 3
24
+
25
+ const STATES = ['idle', 'running', 'completed', 'failed', 'interrupted', 'stopped']
26
+
27
+ const StateSchema = z.object({
28
+ state: z.enum(STATES),
29
+ turn: z.number(),
30
+ /** When the current state began (epoch ms); the folder status picks the oldest. */
31
+ since: z.number(),
32
+ reason: z.string().optional(),
33
+ })
34
+
35
+ const ViewSchema = StateSchema
36
+
37
+ const IDLE = { state: 'idle', turn: 0, since: 0 }
38
+
39
+ /** Map one turn end onto the colour-relevant state. */
40
+ function endedState(reason, turn, since) {
41
+ if (reason === null || typeof reason !== 'object') return { state: 'idle', turn, since }
42
+ switch (reason.kind) {
43
+ case 'completed':
44
+ return { state: 'completed', turn, since }
45
+ case 'forked':
46
+ return { state: 'idle', turn, since }
47
+ case 'error':
48
+ return { state: 'failed', turn, since, reason: reason.error?.code ?? 'error' }
49
+ case 'blocked':
50
+ return { state: 'failed', turn, since, reason: 'blocked' }
51
+ case 'max-tokens':
52
+ return { state: 'failed', turn, since, reason: 'max-tokens' }
53
+ case 'interrupted':
54
+ return { state: 'interrupted', turn, since }
55
+ case 'aborted': {
56
+ // A user stop is not a failure: the sidebar shows it in the neutral tone.
57
+ const kind = reason.reason?.kind
58
+ if (kind === 'user') return { state: 'stopped', turn, since }
59
+ return { state: 'interrupted', turn, since, reason: kind ?? 'aborted' }
60
+ }
61
+ default:
62
+ return { state: 'idle', turn, since }
63
+ }
64
+ }
65
+
66
+ export function apply(ctx) {
67
+ ctx.effect(() => ctx.sessionProjections.register({
68
+ key: KEY,
69
+ stateSchema: StateSchema,
70
+ stateVersion: STATE_VERSION,
71
+ init: () => IDLE,
72
+ apply(state, event) {
73
+ const time = typeof event.time === 'number' ? event.time : Date.now()
74
+ if (event.type === 'turn/start') {
75
+ const turn = event.data?.turn ?? state.turn
76
+ return { state: 'running', turn, since: time }
77
+ }
78
+ if (event.type !== 'turn/end') return state
79
+ return endedState(event.data?.reason, event.data?.turn ?? state.turn, time)
80
+ },
81
+ wire: {
82
+ viewSchema: ViewSchema,
83
+ view(state) {
84
+ return {
85
+ state: state.state,
86
+ turn: state.turn,
87
+ since: state.since,
88
+ ...(typeof state.reason === 'string' && state.reason !== '' ? { reason: state.reason } : {}),
89
+ }
90
+ },
91
+ },
92
+ }))
93
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Folder Sidebar",
4
+ "description": "Workspaces read as folders: click one to enter its session list instead of scrolling every workspace's sessions at once."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "文件夹侧栏",
4
+ "description": "把工作区变成文件夹:侧栏只列工作区,点进去才显示该工作区的会话列表。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "dsh-folder-sidebar",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.17.0",
4
+ "type": "module",
5
+ "description": "Folder-style sidebar for DeepSeek Harness: workspaces read as folders, and clicking one enters its session list instead of scrolling every workspace at once. · DSH 文件夹侧栏:工作区即文件夹,点进去只看该工作区的会话。",
6
+ "license": "MIT",
7
+ "author": "Aliboder",
8
+ "homepage": "https://github.com/Aliboder/dsh-folder-sidebar#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Aliboder/dsh-folder-sidebar.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Aliboder/dsh-folder-sidebar/issues"
15
+ },
16
+ "keywords": [
17
+ "dsh",
18
+ "deepseek-harness",
19
+ "dsh-plugin",
20
+ "cordis",
21
+ "sidebar",
22
+ "workspace",
23
+ "sessions"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20",
27
+ "dsh": ">=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-0 <0.3.0-0"
28
+ },
29
+ "dependencies": {
30
+ "zod": "^4.6.5"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "exports": {
36
+ ".": "./index.js",
37
+ "./client": "./client.js",
38
+ "./locale/*.json": "./locale/*.json",
39
+ "./icon": "./icon.svg",
40
+ "./package.json": "./package.json"
41
+ },
42
+ "files": [
43
+ "index.js",
44
+ "client.js",
45
+ "cordis.patch.yml",
46
+ "locale/*.json",
47
+ "icon.svg",
48
+ "README.md",
49
+ "LICENSE",
50
+ "CHANGELOG.md"
51
+ ],
52
+ "dsh": {
53
+ "bundle": {
54
+ "patch": "./cordis.patch.yml"
55
+ },
56
+ "client": {
57
+ "platform": "web",
58
+ "immediately": true,
59
+ "inject": [
60
+ "@deepseek-ai/dsh-client-ui-conversation"
61
+ ]
62
+ }
63
+ },
64
+ "scripts": {
65
+ "test": "node test/smoke.test.mjs && node test/projection.test.mjs && node test/logic.test.mjs",
66
+ "check": "node --check index.js && node --check client.js",
67
+ "pack:check": "npm pack --dry-run"
68
+ }
6
69
  }