@khorsheed/dsh-library 0.1.0 → 0.1.1
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/CHANGELOG.md +5 -0
- package/lib/client.js +336 -278
- package/lib/tool.js +1 -1
- package/lib/types/client/LibraryPane.js +12 -0
- package/lib/types/client/contract.d.ts +3 -0
- package/lib/types/client/index.js +23 -11
- package/lib/types/client/reveal.d.ts +21 -0
- package/lib/types/client/reveal.js +31 -0
- package/lib/types/client/store.d.ts +2 -0
- package/lib/types/client/store.js +4 -0
- package/lib/types/tools/resource.js +1 -1
- package/package.json +2 -2
- package/lib/profiles-BXM8xNUf.js +0 -1608
- package/lib/profiles-BrbeM-bi.js +0 -1635
- package/lib/profiles-DxtzIUvd.js +0 -1632
- package/lib/profiles-O0ddEit3.js +0 -1631
- package/lib/profiles-Oj-zBAY9.js +0 -1692
- package/lib/profiles-gIEviEuO.js +0 -1610
- package/lib/store-CIcZgTsg.js +0 -1456
- package/lib/store-DfYOmS44.js +0 -1418
package/lib/tool.js
CHANGED
|
@@ -649,7 +649,7 @@ function libraryResourceTool(kit) {
|
|
|
649
649
|
const { service, projectOf, autoBind, scopeParams } = kit;
|
|
650
650
|
return defineTool({
|
|
651
651
|
name: "library_textbook",
|
|
652
|
-
description: "Shelve, correct, reorder or remove a textbook (anything with a contents) — in study order. origin: learner (their own) | picked (a book or course you chose; its path takes only the chapters the goal needs) | authored (you write it: one chapter per lesson, your 讲义 are its text). A real book: verify the edition and contents online and cite the page (tocSource { by: \"web\", url }); never ask for an upload. A correction (resourceId) sends only what changes. A web page a station cites is no textbook — the path names its url.",
|
|
652
|
+
description: "Shelve, correct, reorder or remove a textbook (anything with a contents) — in study order. origin: learner (their own) | picked (a book or course you chose; its path takes only the chapters the goal needs) | authored (you write it: one chapter per lesson, your 讲义 are its text). A real book: verify the edition and contents online and cite the page (tocSource { by: \"web\", url }) — search the title with its author or ISBN, narrowed with site: to catalog or publisher pages (site:book.douban.com, site:worldcat.org, the publisher's domain), one query per edition to tell apart, never a \" in a query; never ask for an upload. A correction (resourceId) sends only what changes. A web page a station cites is no textbook — the path names its url.",
|
|
653
653
|
parameters: {
|
|
654
654
|
...scopeParams,
|
|
655
655
|
op: {
|
|
@@ -32,6 +32,18 @@ import css from './LibraryPane.module.css';
|
|
|
32
32
|
/** The tab body. */
|
|
33
33
|
export function LibraryPane(props) {
|
|
34
34
|
const route = props.useStore(s => s.stack[s.stack.length - 1]);
|
|
35
|
+
const { revealed, actions } = props;
|
|
36
|
+
// A reveal (the session's first library write) lands on its project: taken on
|
|
37
|
+
// mount when the reveal opened this tab, at once when the tab was already open.
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const land = () => {
|
|
40
|
+
const projectId = revealed.take();
|
|
41
|
+
if (projectId !== undefined)
|
|
42
|
+
actions.openProject(projectId);
|
|
43
|
+
};
|
|
44
|
+
land();
|
|
45
|
+
return revealed.subscribe(land);
|
|
46
|
+
}, [revealed, actions]);
|
|
35
47
|
return (_jsxs(PanelRoot, { className: css.root, children: [(() => {
|
|
36
48
|
switch (route.view) {
|
|
37
49
|
case 'today': return _jsx(TodayView, { p: props });
|
|
@@ -12,6 +12,7 @@ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
|
12
12
|
import type { GlobalStandardProps, InjectFace, PropsLocale, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
13
|
import type { LibraryAssessResult, LibraryAssessment, LibraryAttempt, LibraryBinding, LibraryCard, LibraryCardQuery, LibraryProjectPatch, LibraryEvent, LibraryOutcome, LibraryPage, LibraryProject, LibraryProjectSummary, LibraryResource, LibraryReviewRating, LibraryRoadmap, LibraryTemplate, LibraryVerdict } from '../types.ts';
|
|
14
14
|
import type { createLibraryStore } from './store.ts';
|
|
15
|
+
import type { RevealedProject } from './reveal.ts';
|
|
15
16
|
export type { RemoteResult };
|
|
16
17
|
/** A Remote call's answer: the protocol envelope around a domain outcome. */
|
|
17
18
|
type Answer<T> = Promise<RemoteResult<LibraryOutcome<T>>>;
|
|
@@ -140,6 +141,8 @@ export interface LibraryPaneInjected {
|
|
|
140
141
|
}>>;
|
|
141
142
|
/** The session's binding, when it has one. */
|
|
142
143
|
boundTarget: () => Promise<RemoteResult<LibraryBinding | null>>;
|
|
144
|
+
/** The project a reveal asks the pane to land on, taken once (see reveal.ts). */
|
|
145
|
+
revealed: Pick<RevealedProject, 'take' | 'subscribe'>;
|
|
143
146
|
/** Bind the session (null unbinds). */
|
|
144
147
|
bindTarget: (binding: LibraryBinding | null) => Answer<{
|
|
145
148
|
binding: LibraryBinding | null;
|
|
@@ -3,6 +3,7 @@ import { LIBRARY_KIND, LIBRARY_TAB_ID, libraryDefinition } from "./definition.js
|
|
|
3
3
|
import { LibraryPane } from "./LibraryPane.js";
|
|
4
4
|
import { LibraryBindChip } from "./bind/BindChip.js";
|
|
5
5
|
import { createLibraryStore } from "./store.js";
|
|
6
|
+
import { RevealedProject } from "./reveal.js";
|
|
6
7
|
import { en, NS, zh } from "./locales.js";
|
|
7
8
|
import { LibraryTabVisibility, RegistrationToggle, } from "./preset-visibility.js";
|
|
8
9
|
export { LibraryPane };
|
|
@@ -59,6 +60,8 @@ export async function apply(ctx) {
|
|
|
59
60
|
return undefined;
|
|
60
61
|
}
|
|
61
62
|
};
|
|
63
|
+
/** The project the next reveal lands on (one per page; the pane takes it). */
|
|
64
|
+
const revealed = new RevealedProject();
|
|
62
65
|
/** The injected business face, identical for whichever session mounts it. */
|
|
63
66
|
const face = (sessionId) => ({
|
|
64
67
|
listProjects: () => remote.listProjects(),
|
|
@@ -91,6 +94,7 @@ export async function apply(ctx) {
|
|
|
91
94
|
latestActivity: () => remote.latestActivity(),
|
|
92
95
|
boundTarget: () => remote.boundTarget(sessionId),
|
|
93
96
|
bindTarget: binding => remote.bindTarget(sessionId, { binding }),
|
|
97
|
+
revealed,
|
|
94
98
|
sendMessage: text => {
|
|
95
99
|
const input = inputFor(sessionId);
|
|
96
100
|
if (input?.submit === undefined)
|
|
@@ -145,12 +149,13 @@ export async function apply(ctx) {
|
|
|
145
149
|
}, 'library: tab type visibility');
|
|
146
150
|
// Reveal the library beside the conversation that just started using it: a
|
|
147
151
|
// library tool from the on-screen session wrote (a project created, a book
|
|
148
|
-
// shelved, a path drafted) → open the 图书馆 tab
|
|
149
|
-
//
|
|
152
|
+
// shelved, a path drafted) → open the 图书馆 tab on the session's bound
|
|
153
|
+
// project (the write bound it), once per session per page load, so closing
|
|
154
|
+
// it again sticks. The first read is the baseline (writes
|
|
150
155
|
// from before this page loaded never open anything). Degrades to nothing
|
|
151
156
|
// without the sidebar surface or the host half.
|
|
152
157
|
ctx.effect(() => {
|
|
153
|
-
const
|
|
158
|
+
const revealedSessions = new Set();
|
|
154
159
|
let baseline;
|
|
155
160
|
let primed = false;
|
|
156
161
|
const poll = () => {
|
|
@@ -169,15 +174,22 @@ export async function apply(ctx) {
|
|
|
169
174
|
return;
|
|
170
175
|
baseline = write.at;
|
|
171
176
|
const main = mainSessionId(ctx.sessions.list.getSnapshot());
|
|
172
|
-
if (main === undefined || String(main) !== write.id ||
|
|
177
|
+
if (main === undefined || String(main) !== write.id || revealedSessions.has(write.id) || !tabVisibility.show(main))
|
|
173
178
|
return;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
revealedSessions.add(write.id);
|
|
180
|
+
const open = () => {
|
|
181
|
+
try {
|
|
182
|
+
ctx.get('sidebarRight')?.openTab?.(LIBRARY_KIND);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// No on-screen sidebar surface: nothing to reveal.
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
// Land on the project the write bound; without a binding the tab opens on today.
|
|
189
|
+
void remote.boundTarget(main).then((bound) => {
|
|
190
|
+
if (bound.ok && bound.value !== null)
|
|
191
|
+
revealed.reveal(bound.value.projectId);
|
|
192
|
+
}, () => { }).finally(open);
|
|
181
193
|
}, () => { });
|
|
182
194
|
};
|
|
183
195
|
poll();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project the pane should land on when the library reveals itself next to
|
|
3
|
+
* a conversation that just started using it (a project created, a book
|
|
4
|
+
* shelved). The reveal effect sets it from the session's binding before it
|
|
5
|
+
* opens the tab; the pane takes it once — on mount, or at once when the tab
|
|
6
|
+
* is already open — so closing and reopening the tab later starts from today
|
|
7
|
+
* again instead of being pulled back to the project.
|
|
8
|
+
* @module @khorsheed/dsh-library/client/reveal
|
|
9
|
+
*/
|
|
10
|
+
/** One-shot hand-off from the reveal effect to the pane. */
|
|
11
|
+
export declare class RevealedProject {
|
|
12
|
+
private pending;
|
|
13
|
+
private readonly listeners;
|
|
14
|
+
/** Ask the pane to open this project; a mounted pane is told at once. */
|
|
15
|
+
reveal(projectId: string): void;
|
|
16
|
+
/** The project to open, cleared as it is taken; undefined when nothing waits. */
|
|
17
|
+
take(): string | undefined;
|
|
18
|
+
/** Be told when a reveal arrives. */
|
|
19
|
+
subscribe(listener: () => void): () => void;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=reveal.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project the pane should land on when the library reveals itself next to
|
|
3
|
+
* a conversation that just started using it (a project created, a book
|
|
4
|
+
* shelved). The reveal effect sets it from the session's binding before it
|
|
5
|
+
* opens the tab; the pane takes it once — on mount, or at once when the tab
|
|
6
|
+
* is already open — so closing and reopening the tab later starts from today
|
|
7
|
+
* again instead of being pulled back to the project.
|
|
8
|
+
* @module @khorsheed/dsh-library/client/reveal
|
|
9
|
+
*/
|
|
10
|
+
/** One-shot hand-off from the reveal effect to the pane. */
|
|
11
|
+
export class RevealedProject {
|
|
12
|
+
pending;
|
|
13
|
+
listeners = new Set();
|
|
14
|
+
/** Ask the pane to open this project; a mounted pane is told at once. */
|
|
15
|
+
reveal(projectId) {
|
|
16
|
+
this.pending = projectId;
|
|
17
|
+
for (const listener of [...this.listeners])
|
|
18
|
+
listener();
|
|
19
|
+
}
|
|
20
|
+
/** The project to open, cleared as it is taken; undefined when nothing waits. */
|
|
21
|
+
take() {
|
|
22
|
+
const projectId = this.pending;
|
|
23
|
+
this.pending = undefined;
|
|
24
|
+
return projectId;
|
|
25
|
+
}
|
|
26
|
+
/** Be told when a reveal arrives. */
|
|
27
|
+
subscribe(listener) {
|
|
28
|
+
this.listeners.add(listener);
|
|
29
|
+
return () => { this.listeners.delete(listener); };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -93,6 +93,8 @@ export type LibraryActions = {
|
|
|
93
93
|
pop: (draft: LibraryState) => void;
|
|
94
94
|
/** Back to today. */
|
|
95
95
|
home: (draft: LibraryState) => void;
|
|
96
|
+
/** Land on a project's page with today under it, so 「‹」 returns home (the reveal on a first write). */
|
|
97
|
+
openProject: (draft: LibraryState, projectId: string) => void;
|
|
96
98
|
refresh: (draft: LibraryState) => void;
|
|
97
99
|
/** Remember a station's 章节考 settings. */
|
|
98
100
|
setQuizSettings: (draft: LibraryState, key: string, settings: LibraryQuizSettings) => void;
|
|
@@ -27,6 +27,10 @@ export function createLibraryStore() {
|
|
|
27
27
|
d.stack = [TODAY];
|
|
28
28
|
d.rev += 1;
|
|
29
29
|
},
|
|
30
|
+
openProject: (d, projectId) => {
|
|
31
|
+
d.stack = [TODAY, { view: 'project', projectId }];
|
|
32
|
+
d.rev += 1;
|
|
33
|
+
},
|
|
30
34
|
refresh: (d) => { d.rev += 1; },
|
|
31
35
|
setQuizSettings: (d, key, settings) => { d.quiz[key] = { ...d.quiz[key], settings }; },
|
|
32
36
|
setQuizAsk: (d, key, ask) => {
|
|
@@ -13,7 +13,7 @@ export function libraryResourceTool(kit) {
|
|
|
13
13
|
return defineTool({
|
|
14
14
|
name: 'library_textbook',
|
|
15
15
|
description: 'Shelve, correct, reorder or remove a textbook (anything with a contents) — in study order. origin: learner (their own) | picked (a book or course you chose; its path takes only the chapters the goal needs) | authored (you write it: one chapter per lesson, your 讲义 are its text). '
|
|
16
|
-
+ 'A real book: verify the edition and contents online and cite the page (tocSource { by: "web", url }); never ask for an upload. A correction (resourceId) sends only what changes. A web page a station cites is no textbook — the path names its url.',
|
|
16
|
+
+ 'A real book: verify the edition and contents online and cite the page (tocSource { by: "web", url }) — search the title with its author or ISBN, narrowed with site: to catalog or publisher pages (site:book.douban.com, site:worldcat.org, the publisher\'s domain), one query per edition to tell apart, never a " in a query; never ask for an upload. A correction (resourceId) sends only what changes. A web page a station cites is no textbook — the path names its url.',
|
|
17
17
|
parameters: {
|
|
18
18
|
...scopeParams,
|
|
19
19
|
op: { type: 'string', required: true, description: 'add | update | move | remove.' },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khorsheed/dsh-library",
|
|
3
3
|
"description": "Library and learning engine: project-organized close reading of registered books (no upload) — a per-book roadmap the coach drafts and the learner adopts, photographed pages whose marks become knowledge cards, cards that dedupe and track weakness, two-axis quizzes (what they target × what they rest on) and verdict-led unlocking — a Typert Remote data face, a right-Sidebar page tab, and the ./tool composition entry (library_read + one library_<noun> tool per write + library_assess) for agent presets",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"compat": {
|
|
133
133
|
"minHost": "0.1.7-rc.1",
|
|
134
134
|
"notes": "A page-type right-Sidebar tab (minted kind `library`) over a Typert Remote data face, plus the `./tool` composition entry (library_read / library_write / library_assess, the coaching prompt section and a per-turn bound-context line via systemPrompt.context) an agent preset names. Books are registered, never uploaded: the coach verifies the edition's contents on the web; nothing is parsed. State is plain `node:fs` under `$DSH_HOME/state/dsh-library` (atomic temp+rename, per-project lock, append-only JSONL ledgers) — never `ctx.fs`, the session-fenced sandbox (the reader precedent). Session bindings (project / book) are keyed by the calling agent's session id. The tab type self-hides on the preset-composition criterion (the canvas double check: root rows ∪ the current session's preset rows, `@khorsheed/dsh-library/tool`), fail-open on every unreadable path. Photos go through the probed `attachments` service (saveImage for sidebar uploads; readImage only for pages the project recorded), degrading to `unavailable` without it. The tool row's optional hideSections / denyTools quiet a focused preset's scope through scoped prompt sections and tools.restrict, skipping any name the composition does not hold.",
|
|
135
|
-
"verifiedHost": "0.2.1-rc.
|
|
135
|
+
"verifiedHost": "0.2.1-rc.3"
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
}
|