@sparkelf/dsh-knowledge-base 0.0.0-stage → 0.1.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/SKILL.md +147 -0
  3. package/cordis.patch.yml +5 -0
  4. package/i18n/zh.json +10 -0
  5. package/lib/client.js +15976 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/index.js +5724 -0
  8. package/lib/types/api.d.ts +574 -0
  9. package/lib/types/client/KnowledgeAudit.d.ts +39 -0
  10. package/lib/types/client/KnowledgeContextMenu.d.ts +96 -0
  11. package/lib/types/client/KnowledgeCreateDialog.d.ts +134 -0
  12. package/lib/types/client/KnowledgeFileTree.d.ts +88 -0
  13. package/lib/types/client/KnowledgeHome.d.ts +79 -0
  14. package/lib/types/client/KnowledgeIndexing.d.ts +60 -0
  15. package/lib/types/client/KnowledgeListing.d.ts +160 -0
  16. package/lib/types/client/KnowledgeLoading.d.ts +41 -0
  17. package/lib/types/client/KnowledgeMenuIcon.d.ts +27 -0
  18. package/lib/types/client/KnowledgePanel.d.ts +58 -0
  19. package/lib/types/client/KnowledgePermissionOverview.d.ts +99 -0
  20. package/lib/types/client/KnowledgePermissions.d.ts +47 -0
  21. package/lib/types/client/KnowledgeReferencePicker.d.ts +65 -0
  22. package/lib/types/client/KnowledgeSearch.d.ts +83 -0
  23. package/lib/types/client/KnowledgeSectionHeader.d.ts +71 -0
  24. package/lib/types/client/KnowledgeSelectionBar.d.ts +45 -0
  25. package/lib/types/client/KnowledgeSettings.d.ts +96 -0
  26. package/lib/types/client/KnowledgeSettingsSection.d.ts +21 -0
  27. package/lib/types/client/KnowledgeShareDialog.d.ts +70 -0
  28. package/lib/types/client/KnowledgeTemplates.d.ts +43 -0
  29. package/lib/types/client/KnowledgeTrash.d.ts +62 -0
  30. package/lib/types/client/MenuGlyphs.d.ts +37 -0
  31. package/lib/types/client/departmentFolders.d.ts +57 -0
  32. package/lib/types/client/deployTarget.d.ts +34 -0
  33. package/lib/types/client/index.d.ts +27 -0
  34. package/lib/types/client/knowledgeMenuCopy.d.ts +23 -0
  35. package/lib/types/client/locales.d.ts +438 -0
  36. package/lib/types/client/openInSidebar.d.ts +62 -0
  37. package/lib/types/client/ownerLabel.d.ts +39 -0
  38. package/lib/types/client/permissionRows.d.ts +94 -0
  39. package/lib/types/client/roleSummary.d.ts +47 -0
  40. package/lib/types/client/saveAudit.d.ts +87 -0
  41. package/lib/types/client/useFavorites.d.ts +49 -0
  42. package/lib/types/client/usePinnedPaths.d.ts +39 -0
  43. package/lib/types/client/useTreeState.d.ts +46 -0
  44. package/lib/types/client/useViewMode.d.ts +39 -0
  45. package/lib/types/deploy.d.ts +209 -0
  46. package/lib/types/extract.d.ts +144 -0
  47. package/lib/types/index.d.ts +846 -0
  48. package/lib/types/permissions.d.ts +237 -0
  49. package/lib/types/protocol.d.ts +229 -0
  50. package/lib/types/remote.d.ts +105 -0
  51. package/lib/types/trash.d.ts +142 -0
  52. package/lib/types/viewMetadata.d.ts +20 -0
  53. package/lib/types/views.d.ts +923 -0
  54. package/package.json +109 -4
  55. package/skills/kb-organize/SKILL.md +365 -0
  56. package/skills/kb-organize/i18n/zh.json +10 -0
  57. package/README.md +0 -3
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The row context menu for the knowledge tree.
3
+ *
4
+ * A right-click on a row used to jump straight to the permission surface, which meant a
5
+ * reader looking for "save this file" got an operator's dialog instead. The menu is the
6
+ * affordance a file list is expected to have, and it is built on the product's own `Menu`
7
+ * primitive rather than a hand-rolled popover: keyboard walking, Escape, outside click,
8
+ * and post-selection focus return are the primitive's behaviour, and a local copy of that
9
+ * would drift.
10
+ *
11
+ * The menu is positioned from the pointer rather than from an anchor element, which is
12
+ * what the primitive's `getAnchorRect` hook is for: a context menu opens where the reader
13
+ * clicked, not below the row's own control.
14
+ *
15
+ * Every action here is a call to a route that already exists. Sharing is `grant`, the
16
+ * star is `favorites.set`, and paste dispatches on what the clipboard holds: a cut pastes
17
+ * through `move`, a copy through `copy`. Both resolve the destination from the entry's own
18
+ * name, so a paste cannot rename; both carry a directory's whole subtree. The clipboard is
19
+ * a client-side pair of paths and a flag -- nothing about a pending paste is worth a
20
+ * server-side record, and a clipboard that survived a reload would be a paste the reader
21
+ * could no longer see they had armed.
22
+ *
23
+ * @module @sparkelf/dsh-knowledge-base/client
24
+ */
25
+ import { type ShareDialogCopy } from './KnowledgeShareDialog.tsx';
26
+ /** One row the menu can act on. */
27
+ export interface ContextTarget {
28
+ /** Path relative to the knowledge root. */
29
+ relPath: string;
30
+ /** Display name, used as the rename dialog's starting value. */
31
+ name: string;
32
+ /** Whether the row is a directory (renaming one renames the directory). */
33
+ isDir: boolean;
34
+ }
35
+ /** The clipboard the copy/cut entries fill. */
36
+ export interface TreeClipboard {
37
+ /** The path that was copied or cut. */
38
+ relPath: string;
39
+ /** Its display name, so the paste label can name the entry. */
40
+ name: string;
41
+ /** True when the source is removed on paste (cut); false for a copy request. */
42
+ cut: boolean;
43
+ }
44
+ /** Props for the context menu. */
45
+ export interface KnowledgeContextMenuProps {
46
+ /** The row the menu is open on, or null when closed. */
47
+ target: ContextTarget | null;
48
+ /** Where the pointer was, in viewport coordinates. */
49
+ at: {
50
+ x: number;
51
+ y: number;
52
+ } | null;
53
+ /** Copy resolver for this package's namespace. */
54
+ t: (key: string) => string;
55
+ /** Copy for the share dialog, which carries its own key set. */
56
+ shareCopy: ShareDialogCopy;
57
+ /** Close the menu. */
58
+ onClose: () => void;
59
+ /** Open the permission surface for this path. */
60
+ onGrant: (path: string) => void;
61
+ /** Toggle the pin state for this path. */
62
+ onPin: (path: string, pinned: boolean) => void;
63
+ /** Whether this path is currently pinned. */
64
+ pinned: boolean;
65
+ /** Toggle the favourite state for this path. */
66
+ onFavorite: (path: string, favorite: boolean) => void;
67
+ /** Whether this path is currently favourited. */
68
+ favorite: boolean;
69
+ /** Insert a reference to this path into the conversation. */
70
+ onReference: (path: string, isDir: boolean) => void;
71
+ /** Called after a rename succeeds, so the page reloads the tree. */
72
+ onRenamed: () => void;
73
+ /** Called after a delete-to-trash succeeds, so the page reloads the tree. */
74
+ onDeleted: () => void;
75
+ /** What the clipboard currently holds, or null when it is empty. */
76
+ clipboard: TreeClipboard | null;
77
+ /** Put one entry on the clipboard. Recording only; nothing moves until paste. */
78
+ onClipboard: (entry: TreeClipboard | null) => void;
79
+ /**
80
+ * The directory a paste would land in.
81
+ *
82
+ * For a directory row the target is that directory; for a file it is the file's
83
+ * parent. The menu states it so a paste never lands somewhere the reader cannot see,
84
+ * and `null` means the destination is unknown, which disables the entry rather than
85
+ * guessing.
86
+ */
87
+ pasteInto: string | null;
88
+ /** Called after a paste succeeds, so the page reloads both affected levels. */
89
+ onMoved: () => void;
90
+ }
91
+ /**
92
+ * The knowledge tree's row context menu.
93
+ * @param props - the target row, the pointer position, and the actions.
94
+ * @returns The menu, or nothing when it is closed.
95
+ */
96
+ export declare function KnowledgeContextMenu(props: KnowledgeContextMenuProps): import("react").JSX.Element;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * One official dialog behind the Home surface's three create/upload actions, plus the
3
+ * destination picker and name field they all share.
4
+ *
5
+ * The reference had a dropdown per card whose rows opened a browser `window.prompt` for
6
+ * a name and dropped the result into `global/`. Two things were wrong with that: a
7
+ * prompt cannot show where the file will land, and it cannot offer to change it -- so
8
+ * every new document silently published to the one bucket everybody reads.
9
+ *
10
+ * All three actions therefore share one dialog, because they ask the same two questions
11
+ * (what is it called, and where does it go) and differ only in what they write: `create`
12
+ * for a document, `create` with `kind: 'directory'` for a folder, and `upload` per file
13
+ * for an upload. Keeping them together means the validation, the destination default and
14
+ * the keyboard contract cannot drift apart between them.
15
+ *
16
+ * The dialog is a `Modal` and the fields are `Input`/`Button` from the primitives -- no
17
+ * hand-built layer -- so Escape, the mask, focus return and the card chrome all come
18
+ * from the same implementation every other DSH dialog uses. The directory picker reads
19
+ * the live tree one level at a time through the Host's own listing route (`KB_API.list`),
20
+ * so it can never offer a directory the caller may not read, and it takes a
21
+ * `defaultPath` chosen by {@link defaultDestination} rather than guessing.
22
+ *
23
+ * @module @sparkelf/dsh-knowledge-base/client
24
+ */
25
+ /**
26
+ * Where a create or upload should land when the reader has not said otherwise.
27
+ *
28
+ * The order is the whole point of this function, and it is deliberately not
29
+ * "global first":
30
+ *
31
+ * 1. The directory the reader is looking at. If they navigated into
32
+ * `department/OPS/specs` and then pressed New, that is where they mean -- a
33
+ * create that jumps elsewhere makes the tree they are reading a lie.
34
+ * 2. The reader's own personal directory. Without a current directory this is the
35
+ * only bucket that is theirs by construction, so it is the safe default: a file
36
+ * created there cannot surprise a colleague.
37
+ * 3. `global`, last, and only as a last resort. It is the shared bucket, so
38
+ * defaulting to it is how a private draft becomes a team announcement.
39
+ *
40
+ * @param currentDir - the tree-relative directory the reader is browsing, if any.
41
+ * @param userId - the caller's DataOps id, from the status route.
42
+ * @returns A tree-relative directory that exists or is expected to.
43
+ */
44
+ export declare function defaultDestination(currentDir: string | undefined, userId: string | undefined): string;
45
+ /** Props for the destination picker. */
46
+ export interface KnowledgeDestinationPickerProps {
47
+ /** Tree-relative directory currently selected. */
48
+ value: string;
49
+ /** Called with a new tree-relative directory. */
50
+ onChange: (path: string) => void;
51
+ /** Locale-bound copy resolver. */
52
+ t: (key: string) => string;
53
+ /** Disables the control while the dialog is submitting. */
54
+ disabled?: boolean;
55
+ }
56
+ /**
57
+ * A breadcrumb-style directory chooser: the current path, a way back up, and the
58
+ * subdirectories of the level being shown.
59
+ * @param props - the selected path and its change handler.
60
+ * @returns The picker.
61
+ */
62
+ export declare function KnowledgeDestinationPicker(props: KnowledgeDestinationPickerProps): import("react").JSX.Element;
63
+ /** Props for the name field, shared by the document and folder forms. */
64
+ export interface KnowledgeNameFieldProps {
65
+ /** What the reader has typed so far. */
66
+ value: string;
67
+ /** Called on every keystroke. */
68
+ onChange: (next: string) => void;
69
+ /** Locale-bound copy resolver. */
70
+ t: (key: string) => string;
71
+ /** Placeholder naming the kind being created. */
72
+ placeholder: string;
73
+ /** Set while submitting. */
74
+ disabled?: boolean;
75
+ /** A validation message to show under the field. */
76
+ error?: string;
77
+ }
78
+ /**
79
+ * Reject a name this tree cannot store.
80
+ *
81
+ * The rule mirrors the Host's `isContainedRelPath`: a name may not be empty, may not
82
+ * contain a separator (that would silently make the "name" a path), and may not be
83
+ * a traversal segment. Rejecting it here turns a refused request into an inline
84
+ * sentence before the reader presses Create.
85
+ * @param name - the candidate name, without any directory part.
86
+ * @returns A locale key naming the problem, or undefined when the name is fine.
87
+ */
88
+ export declare function validateEntryName(name: string): 'createNameEmpty' | 'createNameSeparator' | 'createNameTraversal' | undefined;
89
+ /**
90
+ * The name field with its inline validation.
91
+ * @param props - the field's state.
92
+ * @returns The labelled input.
93
+ */
94
+ export declare function KnowledgeNameField(props: KnowledgeNameFieldProps): import("react").JSX.Element;
95
+ /**
96
+ * Which of the three flows the dialog is running.
97
+ *
98
+ * `upload-file` and `upload-folder` differ only in how the browser's picker is
99
+ * configured (`webkitdirectory`), not in what the dialog asks; the destination is a
100
+ * single choice for the whole selection, which is what "choose where these go"
101
+ * means.
102
+ */
103
+ export type CreateMode = 'new-doc' | 'new-folder' | 'upload-file' | 'upload-folder';
104
+ /** One file a reader chose, with the path it should land at. */
105
+ export interface PendingUpload {
106
+ /** The file as the browser handed it over. */
107
+ file: File;
108
+ /** Its name relative to the picked root: a folder upload keeps its shape. */
109
+ relative: string;
110
+ }
111
+ /** Props for the dialog. */
112
+ export interface KnowledgeCreateDialogProps {
113
+ /** Whether the dialog is showing. */
114
+ open: boolean;
115
+ /** Which flow to run. */
116
+ mode: CreateMode;
117
+ /** Close without writing anything. */
118
+ onClose: () => void;
119
+ /** The directory the dialog should start in, from {@link defaultDestination}. */
120
+ defaultPath: string;
121
+ /** Locale-bound copy resolver. */
122
+ t: (key: string) => string;
123
+ /**
124
+ * Report the outcome and refresh the caller's listing.
125
+ * @param message - a locale-bound sentence to show under the cards.
126
+ */
127
+ onDone: (message: string) => void;
128
+ }
129
+ /**
130
+ * The create/upload dialog.
131
+ * @param props - the flow, its starting directory, and the outcome channel.
132
+ * @returns The dialog, or null while closed.
133
+ */
134
+ export declare function KnowledgeCreateDialog(props: KnowledgeCreateDialogProps): import("react").JSX.Element;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The knowledge tree, drawn by the product's own file explorer.
3
+ *
4
+ * The listing next door is a hand-built copy of `dsh-better-sidebar`'s explorer: its CSS
5
+ * module says so ("Copied from the file explorer in dsh-better-sidebar", with each metric
6
+ * measured against `.explorerRow`). A copy drifts -- the explorer gains sort controls,
7
+ * inline rename, an upload strip, git tones, an open-with submenu, none of which arrive
8
+ * in a copy -- so this component renders the REAL explorer instead, pointed at the
9
+ * knowledge root.
10
+ *
11
+ * That is possible because `/workspace/kb` is a symlink into the caller's view: the
12
+ * explorer's `cwd` is a plain absolute path, so pointing it there reads the knowledge tree
13
+ * through exactly the machinery that reads any other workspace directory. Measured before
14
+ * writing this: `POST /sidebar/api/fs.trees {cwd:'/workspace/kb'}` answers the tree's
15
+ * entries, so no new route and no second file API are involved.
16
+ *
17
+ * What the explorer does NOT know about is the knowledge base's own metadata files
18
+ * (`.kb-audit.jsonl`, `.kb-settings.json`, `.kb-tree-state.json`, `.pinned.json`,
19
+ * `.view-mode.json`, `.kb-favorites.json`, `.kb-trash.json`). They live in the view so
20
+ * every user's copy carries them, and they are neither knowledge documents nor anything
21
+ * a reader should rename. They are filtered with the `exclude` prop, which the host
22
+ * applies to the listing -- so they never arrive rather than being hidden after the fact.
23
+ *
24
+ * @module @sparkelf/dsh-knowledge-base/client
25
+ */
26
+ import type { UploadItem } from 'dsh-better-sidebar/src/client/upload.ts';
27
+ import { type DepartmentPath } from './departmentFolders.ts';
28
+ export interface KnowledgeFileTreeProps {
29
+ /** The conversation this tree belongs to; the explorer keys its fs requests by it. */
30
+ sessionId: string;
31
+ /** Absolute path of the knowledge root inside the workspace. */
32
+ root: string;
33
+ /** Directories the reader has expanded, by absolute path. */
34
+ expanded: readonly string[];
35
+ /** Files highlighted by a "show in folder" reveal, by absolute path. */
36
+ revealed: readonly string[];
37
+ /** Toggle one directory open or closed. */
38
+ onToggle: (path: string) => void;
39
+ /** Open one file for preview. */
40
+ onOpenFile: (path: string) => void;
41
+ /** Insert a reference to one path into the conversation draft. */
42
+ onReferenceFile: (path: string, isDir: boolean) => void;
43
+ /** Upload files into a directory (absolute, inside the knowledge root). */
44
+ onUploadRequest: (dir: string, items: UploadItem[]) => void;
45
+ /** True while an upload is in flight. */
46
+ busy: boolean;
47
+ /** Bump to reload the visible set after a change made elsewhere. */
48
+ refreshTick: number;
49
+ /** Called after the explorer's own inline rename lands. */
50
+ onPathRenamed?: (oldPath: string, newPath: string) => void;
51
+ /** Called after the explorer's own delete lands. */
52
+ onPathDeleted?: (path: string, isDir: boolean) => void;
53
+ /** Whether the owning surface is on screen. */
54
+ visible?: boolean;
55
+ /**
56
+ * Say the three buckets in the reader's language.
57
+ *
58
+ * `global` / `department` / `personal` are the folders' names on disk, and they have
59
+ * to stay that way: they are path segments the deployment shares and the caller's
60
+ * view is built from them. What the reader is being told by that folder is not its
61
+ * path but its AUDIENCE -- who may see what is inside -- so the row prints the
62
+ * audience and the path is left alone.
63
+ *
64
+ * Keyed by the tree-relative path, so the map can be built from copy alone.
65
+ */
66
+ bucketLabels?: Readonly<Record<string, string>>;
67
+ /**
68
+ * The reader's own organization chain, so department folders print their NAME.
69
+ *
70
+ * The department bucket stores folders by code (`nanning-psb`), because a code is a
71
+ * stable slug: renaming a department cannot move a document. The reader, though,
72
+ * knows their organization by name, so those same rows have to print
73
+ * 「南宁市公安局」 rather than the slug.
74
+ *
75
+ * Only the caller's own chain is translated, and only within the department bucket.
76
+ * A peer department's folder keeps printing its code: this build has no name for it,
77
+ * and the whole organization tree is readable only by an administrator. Deriving a
78
+ * "name" from the code would put an invented label in front of the reader, which is
79
+ * worse than the code they can at least match against their own department.
80
+ */
81
+ departmentPath?: DepartmentPath;
82
+ }
83
+ /**
84
+ * The knowledge tree, rendered by the product's file explorer.
85
+ * @param props - the root, the reader's expansion state, and the actions.
86
+ * @returns The explorer's tree, rooted at the knowledge directory.
87
+ */
88
+ export declare function KnowledgeFileTree(props: KnowledgeFileTreeProps): import("react").JSX.Element;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The Home surface: the page's landing view.
3
+ *
4
+ * Three ways to start, over a tabbed list of what the reader already has. The tabs
5
+ * select the SET, and the table shows that set's rows; nothing here invents data,
6
+ * so a tab with no rows says so rather than filling the table with placeholders.
7
+ *
8
+ * The listing comes from the same route the tree uses. Categorising a document as
9
+ * "owned by me" or "shared with me" would need an owner per file, and the
10
+ * knowledge tree carries none: it is a directory of documents whose authorization
11
+ * lives in DataOps grants. So the tabs narrow by what the tree can actually
12
+ * answer, and the Owner column is drawn from the grant data only when a caller
13
+ * supplies it. Filling that column with a guess would be worse than leaving it
14
+ * blank.
15
+ *
16
+ * @module @sparkelf/dsh-knowledge-base/client
17
+ */
18
+ /** Props for the Home surface. */
19
+ export interface KnowledgeHomeProps {
20
+ /** Locale-bound copy resolver. */
21
+ t: (key: string) => string;
22
+ /** Locale tag, so dates match the reader's language. */
23
+ locale?: string;
24
+ /** Disabled when the tree is not mounted, so the cards cannot promise a tree. */
25
+ mountPoint: string;
26
+ /** Open one document in the preview. */
27
+ onOpen: (path: string) => void;
28
+ /**
29
+ * The caller's DataOps id, when the status route reported one.
30
+ *
31
+ * Used only to choose where a create lands by default: with no current directory
32
+ * the reader's own `personal/<id>` folder is the one place a new draft cannot
33
+ * surprise a colleague, so it is preferred over `global`.
34
+ */
35
+ callerId?: string;
36
+ /** The caller's own department code, so the owner cell can name it instead of a slug. */
37
+ departmentCode?: string;
38
+ /** The caller's own department name; the code stands in when this is absent. */
39
+ departmentName?: string;
40
+ /**
41
+ * The tab whose card should take focus on mount.
42
+ *
43
+ * Set when a reader arrives here from the tree's empty state, which is asking
44
+ * them to start a document: the blank-page card is the answer to that question,
45
+ * so it is the one that should be reachable by keyboard immediately.
46
+ */
47
+ focusNew?: boolean;
48
+ /**
49
+ * Star or unstar one row.
50
+ *
51
+ * The Home table is the surface a reader actually starts from, so the context menu
52
+ * lives here as well as on the tree. Omitted by a caller with no favourites store,
53
+ * which keeps the menu's star entry out rather than offering a write that cannot land.
54
+ */
55
+ onFavorite?: (path: string, favorite: boolean) => void;
56
+ /** Whether one path is currently favourited, so the row can draw its star. */
57
+ isFavorite?: (path: string) => boolean;
58
+ /** Open the permission surface for one path. */
59
+ onGrant?: (path: string) => void;
60
+ /** Insert a reference to this path into the conversation. */
61
+ onReference?: (path: string, isDir: boolean) => void;
62
+ /** Pin or unpin one row, offered by the same menu the tree uses. */
63
+ onPin?: (path: string, pinned: boolean) => void;
64
+ /** Whether one path is currently pinned. */
65
+ isPinned?: (path: string) => boolean;
66
+ /**
67
+ * Called after any menu action that changed the tree.
68
+ *
69
+ * The table holds its own rows, so a rename/delete/paste has to be followed by a
70
+ * re-read; the page owns the tick that triggers it.
71
+ */
72
+ onTreeChanged?: () => void;
73
+ }
74
+ /**
75
+ * Render the Home surface.
76
+ * @param props - copy resolver, mount state, and the open callback.
77
+ * @returns The Home view.
78
+ */
79
+ export declare function KnowledgeHome(props: KnowledgeHomeProps): import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The indexing surface of the management page.
3
+ *
4
+ * There is no separate index. Retrieval reads the mounted tree directly with grep
5
+ * and read, so a "rebuild" is a recount of what is mounted, and it answers
6
+ * synchronously. Presenting it as a background job would invent a queue the plugin
7
+ * does not have, so this surface says what it is in its own copy instead of
8
+ * implying an index process.
9
+ *
10
+ * The table reports per-directory facts the Host derived from the filesystem:
11
+ * how many files are below each directory, when the newest one changed, and
12
+ * whether the caller can read it. Those are measurements, not estimates, and a
13
+ * directory whose numbers are not known is absent rather than shown as zero.
14
+ *
15
+ * @module @sparkelf/dsh-knowledge-base/client
16
+ */
17
+ import { type ReactNode } from 'react';
18
+ import { type KnowledgeStatusView } from '../api.ts';
19
+ /** One directory's measured shape, as the Host reports it. */
20
+ export interface IndexDirectory {
21
+ /** Path relative to the mount point; empty string means the root itself. */
22
+ relPath: string;
23
+ /** Files below this directory, counted recursively. */
24
+ fileCount: number;
25
+ /** Newest modification instant in Unix ms, absent when nothing could be read. */
26
+ mtimeMs?: number;
27
+ /** Whether this directory's own entries could be listed. */
28
+ readable: boolean;
29
+ /**
30
+ * Whether some part of the subtree could not be read, so `fileCount` is a lower
31
+ * bound rather than a total.
32
+ *
33
+ * Absent from an older Host, which is why it is optional here: a missing flag is
34
+ * treated as "not known to be partial" rather than as "complete", so the column
35
+ * never claims more than the Host measured.
36
+ */
37
+ partial?: boolean;
38
+ }
39
+ /** Props for the indexing surface. */
40
+ export interface KnowledgeIndexingProps {
41
+ /**
42
+ * Publishes this page's primary control to the panel header.
43
+ *
44
+ * The header is the only band at the top of the page, so a page-level action has
45
+ * to be handed up rather than drawn in a second title row.
46
+ */
47
+ onAction?: (action: ReactNode) => void;
48
+ /** Status the page already loaded, used for the first paint. */
49
+ status: KnowledgeStatusView;
50
+ /** Called after a rescan, so the page can refresh what it shows. */
51
+ onChanged: () => void;
52
+ /** Locale-bound copy for this package's namespace. */
53
+ t: (key: string) => string;
54
+ }
55
+ /**
56
+ * Render the index state as a per-directory table.
57
+ * @param props - the loaded status and a refresh callback.
58
+ * @returns The indexing surface.
59
+ */
60
+ export declare function KnowledgeIndexing(props: KnowledgeIndexingProps): import("react").JSX.Element;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * A directory listing with the modification time each entry carries.
3
+ *
4
+ * The row is the file explorer's own row from `dsh-better-sidebar`, copied from
5
+ * `src/client/FileTree.tsx` (`FileRow`/`DirRow`) and `src/client/sidebar.module.css`
6
+ * (`.explorerRow`): the same `div[role=button]`, the same 14px glyph, the same
7
+ * ellipsised name, the same trailing modified time that yields its right edge to
8
+ * the same hover-revealed `@` reference button, and the same `depth * 22 + 6`
9
+ * indent. A reader sees that tree in this window's right sidebar, so a second
10
+ * design beside it reads as bolted on -- which is why this is a copy rather than
11
+ * a re-drawing.
12
+ *
13
+ * What the borrowed tree cannot carry is why this list exists. Its host entry
14
+ * shape (`FsEntry`: name, path, isDir, hidden, isSymlink, broken) has no
15
+ * timestamp, so a row built from it can never show when a document last changed;
16
+ * and its rows decide visibility from a shared curated store rather than from
17
+ * per-user grants. This panel reads the same directories through the plugin's own
18
+ * route, which stats each child, and its row carries a right-click into the grant
19
+ * form the tree has no place for.
20
+ *
21
+ * @module @sparkelf/dsh-knowledge-base/client
22
+ */
23
+ import { type ReactNode } from 'react';
24
+ /** Props for the listing panel. */
25
+ export interface KnowledgeListingProps {
26
+ /** Locale-bound copy resolver. */
27
+ t: (key: string) => string;
28
+ /** Called with a chosen path so the page can show its preview. */
29
+ onOpen: (path: string) => void;
30
+ /** Re-read trigger, so a rescan from elsewhere refreshes this too. */
31
+ refreshTick?: number;
32
+ /** Locale tag, so dates match the reader's language. */
33
+ locale?: string;
34
+ /**
35
+ * Open the permission surface for one path.
36
+ *
37
+ * Exists because the borrowed tree's context menu is built inside it with no way
38
+ * to add a row, so the grant entry lives here instead: this is the plugin's own
39
+ * listing, where a right-click can mean what the knowledge base needs it to mean.
40
+ */
41
+ onGrant?: (path: string) => void;
42
+ /**
43
+ * Insert one row's reference into the conversation draft.
44
+ *
45
+ * The borrowed tree reveals this affordance on hover; the listing mirrors that so
46
+ * a reader gets the same `@` action from either tree.
47
+ */
48
+ onReference?: (path: string, isDir: boolean) => void;
49
+ /**
50
+ * Pin or unpin one row.
51
+ *
52
+ * Omitted by a caller that has no pin store, which is how the search results list
53
+ * stays free of an action that would only make sense inside the tree.
54
+ */
55
+ onPin?: (path: string, pinned: boolean) => void;
56
+ /** Whether one path is currently pinned, so the row can mark it. */
57
+ isPinned?: (path: string) => boolean;
58
+ /**
59
+ * Star or unstar one row.
60
+ *
61
+ * Omitted by a caller with no favourites store, which is how the search results list
62
+ * stays free of an entry that would only make sense inside the tree. A folder is
63
+ * favoured the same way a file is: the star marks a row, not a document.
64
+ */
65
+ onFavorite?: (path: string, favorite: boolean) => void;
66
+ /** Whether one path is currently favourited, so the row can draw its star. */
67
+ isFavorite?: (path: string) => boolean;
68
+ /**
69
+ * The path a paste would land in, given the row this menu was opened on.
70
+ *
71
+ * A directory row pastes INTO itself; a file row pastes beside itself. Kept a
72
+ * callback rather than derived in the menu because only the listing knows whether a
73
+ * row is the tree's root, which has no parent to paste beside.
74
+ */
75
+ pasteTargetFor?: (relPath: string, isDir: boolean) => string | null;
76
+ /**
77
+ * Called after a menu-driven delete or paste succeeded.
78
+ *
79
+ * Both change paths this component is currently holding -- a delete removes one, a
80
+ * paste adds one and empties another -- so the page has to reload rather than the
81
+ * component guessing which levels moved.
82
+ */
83
+ onTreeChanged?: () => void;
84
+ /**
85
+ * Called after a rename succeeded, so the page reloads the tree.
86
+ *
87
+ * The listing cannot reload itself: it reads through `refreshTick`, which the page
88
+ * owns, and a rename changes the paths this component is currently holding.
89
+ */
90
+ onRenamed?: () => void;
91
+ /**
92
+ * Render one row's action as "unpin" rather than "pin".
93
+ *
94
+ * The pinned band lists paths that are already pinned, so every row there offers
95
+ * the opposite action; deriving it from a flag keeps one row shape for both bands.
96
+ */
97
+ unpinOnly?: boolean;
98
+ /**
99
+ * Narrow the tree to rows whose name contains this text.
100
+ *
101
+ * Filtering happens over the rows already read, so it costs no request: the tree
102
+ * section header's search is the in-place narrowing, while the rail's field is the
103
+ * cross-directory search. A filter that matched nothing read as an empty tree, so
104
+ * the two empty states are kept apart.
105
+ */
106
+ filter?: string;
107
+ /**
108
+ * Start with every directory expanded.
109
+ *
110
+ * Used by the pinned band, where a pinned folder is only useful if its contents
111
+ * are already visible.
112
+ */
113
+ expandAll?: boolean;
114
+ /**
115
+ * The action offered when the tree genuinely holds no documents.
116
+ *
117
+ * Distinct from a filter that matched nothing: an empty tree wants a way to make
118
+ * a document, while an unmatched filter wants a different search term.
119
+ */
120
+ emptyAction?: ReactNode;
121
+ /**
122
+ * Move one entry into a directory, or pin it when the target is the pin band.
123
+ *
124
+ * Omitted by a caller that must stay read-only, which is how the search results
125
+ * list keeps its rows free of a gesture the Host would refuse there.
126
+ */
127
+ onMove?: (from: string, toDirectory: string) => void;
128
+ /**
129
+ * The layout to render. `list` is the tree; `grid` is the document cards.
130
+ *
131
+ * Defaults to the tree so a caller that does not persist a preference behaves
132
+ * exactly as it did before the card view existed.
133
+ */
134
+ viewMode?: 'list' | 'grid';
135
+ /**
136
+ * Enter multi-select. A callback rather than internal state because the batch
137
+ * bar's actions are the host's (they move and pin through the same routes a
138
+ * single drag uses), so the selection has to be visible to it.
139
+ */
140
+ onSelectionChange?: (paths: readonly string[]) => void;
141
+ /** The current selection, so a batch action can act on it. */
142
+ selection?: readonly string[];
143
+ }
144
+ /**
145
+ * The drag payload's MIME type.
146
+ *
147
+ * Exported because the drop target that pins is NOT in this component: the pin
148
+ * band lives in the panel, and the two have to agree on the name of the thing
149
+ * being carried. The native drag payload is used rather than lifted React state
150
+ * for exactly that reason -- it crosses the component boundary the gesture
151
+ * crosses, without the panel having to hold a second copy of the tree's state.
152
+ */
153
+ export declare const KB_DRAG_MIME = "application/x-dsh-kb-path";
154
+ /**
155
+ * Render the listing for the mount's top level, and one level per expanded
156
+ * directory.
157
+ * @param props - copy resolver and the open callback.
158
+ * @returns The listing panel.
159
+ */
160
+ export declare function KnowledgeListing(props: KnowledgeListingProps): import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The loading state, as a book whose page turns.
3
+ *
4
+ * The knowledge base is a library of documents, so a turning page says what is
5
+ * happening in the product's own terms rather than with a generic spinner. The
6
+ * animation is CSS and inline SVG: no animation library, because this plugin ships
7
+ * as one bundled file and a dependency would grow it and add a supply-chain surface
8
+ * for a decoration.
9
+ *
10
+ * The motion is a real 3D turn rather than a sequence of drawn frames: the leaf is a
11
+ * zero-thickness plane inside a `perspective` container, rotated about the spine with
12
+ * `rotateY`. That keeps the whole effect to two keyframes and makes the direction of
13
+ * travel read correctly at any moment, which a frame sequence does not.
14
+ *
15
+ * @module @sparkelf/dsh-knowledge-base/client
16
+ */
17
+ /** Props for the loading surface. */
18
+ export interface KnowledgeLoadingProps {
19
+ /**
20
+ * What is being loaded, in the reader's language.
21
+ *
22
+ * Optional because a card or a table cell may have no label of its own; the
23
+ * animation then stands alone and the accessible name falls back to the shared
24
+ * loading sentence.
25
+ */
26
+ label?: string;
27
+ /**
28
+ * Accessible name for the animation itself.
29
+ *
30
+ * Required rather than derived: the animation is decorative, so it must either be
31
+ * hidden from assistive technology or carry a name, and hiding it silently would
32
+ * leave a screen-reader user with no announcement that anything is loading.
33
+ */
34
+ announce?: string;
35
+ }
36
+ /**
37
+ * Render the book-turning loading animation.
38
+ * @param props - the optional label and the accessible announcement.
39
+ * @returns The centred loading surface.
40
+ */
41
+ export declare function KnowledgeLoading(props: KnowledgeLoadingProps): import("react").JSX.Element;