@sparkelf/dsh-knowledge-base 0.0.0-stage → 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/LICENSE +21 -0
- package/SKILL.md +147 -0
- package/cordis.patch.yml +5 -0
- package/i18n/zh.json +10 -0
- package/lib/client.js +18308 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +6005 -0
- package/lib/types/api.d.ts +620 -0
- package/lib/types/client/KnowledgeAudit.d.ts +39 -0
- package/lib/types/client/KnowledgeContextMenu.d.ts +121 -0
- package/lib/types/client/KnowledgeCreateDialog.d.ts +134 -0
- package/lib/types/client/KnowledgeFileTree.d.ts +117 -0
- package/lib/types/client/KnowledgeHome.d.ts +102 -0
- package/lib/types/client/KnowledgeIndexing.d.ts +60 -0
- package/lib/types/client/KnowledgeListing.d.ts +174 -0
- package/lib/types/client/KnowledgeLoading.d.ts +41 -0
- package/lib/types/client/KnowledgeMenuIcon.d.ts +27 -0
- package/lib/types/client/KnowledgePanel.d.ts +58 -0
- package/lib/types/client/KnowledgePermissionOverview.d.ts +147 -0
- package/lib/types/client/KnowledgePermissions.d.ts +47 -0
- package/lib/types/client/KnowledgeProgressPanel.d.ts +53 -0
- package/lib/types/client/KnowledgeReferencePicker.d.ts +81 -0
- package/lib/types/client/KnowledgeSearch.d.ts +83 -0
- package/lib/types/client/KnowledgeSectionHeader.d.ts +71 -0
- package/lib/types/client/KnowledgeSelectionBar.d.ts +45 -0
- package/lib/types/client/KnowledgeSettings.d.ts +96 -0
- package/lib/types/client/KnowledgeSettingsSection.d.ts +21 -0
- package/lib/types/client/KnowledgeShareDialog.d.ts +70 -0
- package/lib/types/client/KnowledgeTemplates.d.ts +43 -0
- package/lib/types/client/KnowledgeTrash.d.ts +62 -0
- package/lib/types/client/MenuGlyphs.d.ts +37 -0
- package/lib/types/client/auditPaging.d.ts +40 -0
- package/lib/types/client/departmentFolders.d.ts +57 -0
- package/lib/types/client/deployTarget.d.ts +34 -0
- package/lib/types/client/index.d.ts +40 -0
- package/lib/types/client/insertReadiness.d.ts +60 -0
- package/lib/types/client/knowledgeMenuCopy.d.ts +23 -0
- package/lib/types/client/locales.d.ts +505 -0
- package/lib/types/client/openInSidebar.d.ts +62 -0
- package/lib/types/client/ownerLabel.d.ts +39 -0
- package/lib/types/client/paging.d.ts +69 -0
- package/lib/types/client/permissionRows.d.ts +265 -0
- package/lib/types/client/progressRules.d.ts +54 -0
- package/lib/types/client/referenceTargets.d.ts +60 -0
- package/lib/types/client/roleSummary.d.ts +47 -0
- package/lib/types/client/saveAudit.d.ts +87 -0
- package/lib/types/client/shareRelations.d.ts +81 -0
- package/lib/types/client/useFavorites.d.ts +49 -0
- package/lib/types/client/useFileDrop.d.ts +171 -0
- package/lib/types/client/useFileDropHook.d.ts +64 -0
- package/lib/types/client/usePinnedPaths.d.ts +39 -0
- package/lib/types/client/useTreeState.d.ts +46 -0
- package/lib/types/client/useViewMode.d.ts +39 -0
- package/lib/types/deploy.d.ts +209 -0
- package/lib/types/extract.d.ts +144 -0
- package/lib/types/extractionQueue.d.ts +154 -0
- package/lib/types/index.d.ts +899 -0
- package/lib/types/permissions.d.ts +237 -0
- package/lib/types/protocol.d.ts +229 -0
- package/lib/types/remote.d.ts +105 -0
- package/lib/types/trash.d.ts +142 -0
- package/lib/types/viewMetadata.d.ts +20 -0
- package/lib/types/views.d.ts +923 -0
- package/package.json +109 -4
- package/skills/kb-organize/SKILL.md +365 -0
- package/skills/kb-organize/i18n/zh.json +10 -0
- package/README.md +0 -3
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
/**
|
|
38
|
+
* Every entry taken, in the order they were selected.
|
|
39
|
+
*
|
|
40
|
+
* A list rather than one path because a multi-row copy is a normal gesture and the
|
|
41
|
+
* paste has to put all of them somewhere. A single-row copy is a one-element list,
|
|
42
|
+
* which is why there is no separate single-row clipboard: one shape means the paste
|
|
43
|
+
* path cannot be right for one row and wrong for five.
|
|
44
|
+
*/
|
|
45
|
+
entries: readonly {
|
|
46
|
+
relPath: string;
|
|
47
|
+
name: string;
|
|
48
|
+
}[];
|
|
49
|
+
/** True when the source is removed on paste (cut); false for a copy request. */
|
|
50
|
+
cut: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Props for the context menu. */
|
|
53
|
+
export interface KnowledgeContextMenuProps {
|
|
54
|
+
/** The row the menu is open on, or null when closed. */
|
|
55
|
+
target: ContextTarget | null;
|
|
56
|
+
/**
|
|
57
|
+
* Every row the menu acts on, when the reader right-clicked inside a multi-selection.
|
|
58
|
+
*
|
|
59
|
+
* Absent or shorter than two means single-row behaviour, which is why the component
|
|
60
|
+
* stays one menu rather than gaining a batch twin: the entries, the handlers and the
|
|
61
|
+
* clipboard are identical, and only the LABELS change to say how many rows they will
|
|
62
|
+
* touch. A second menu is how the two surfaces drifted apart before.
|
|
63
|
+
*
|
|
64
|
+
* `target` is still the row under the pointer -- it anchors the menu and supplies the
|
|
65
|
+
* name used by rename/download, which have no batch meaning.
|
|
66
|
+
*/
|
|
67
|
+
selection?: readonly ContextTarget[];
|
|
68
|
+
/** Where the pointer was, in viewport coordinates. */
|
|
69
|
+
at: {
|
|
70
|
+
x: number;
|
|
71
|
+
y: number;
|
|
72
|
+
} | null;
|
|
73
|
+
/** Copy resolver for this package's namespace. */
|
|
74
|
+
t: (key: string) => string;
|
|
75
|
+
/** Copy for the share dialog, which carries its own key set. */
|
|
76
|
+
shareCopy: ShareDialogCopy;
|
|
77
|
+
/** Close the menu. */
|
|
78
|
+
onClose: () => void;
|
|
79
|
+
/** Open the permission surface for this path. */
|
|
80
|
+
onGrant: (path: string) => void;
|
|
81
|
+
/** Toggle the pin state for this path. */
|
|
82
|
+
onPin: (path: string, pinned: boolean) => void;
|
|
83
|
+
/** Whether this path is currently pinned. */
|
|
84
|
+
pinned: boolean;
|
|
85
|
+
/** Toggle the favourite state for this path. */
|
|
86
|
+
onFavorite: (path: string, favorite: boolean) => void;
|
|
87
|
+
/** Whether this path is currently favourited. */
|
|
88
|
+
favorite: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Insert references into a conversation, via the picker.
|
|
91
|
+
*
|
|
92
|
+
* `targets` is present only for a multi-row selection; the single-row case passes the
|
|
93
|
+
* one path. The picker renders one dialog either way.
|
|
94
|
+
*/
|
|
95
|
+
onReference: (path: string, isDir: boolean, targets?: readonly ContextTarget[]) => void;
|
|
96
|
+
/** Called after a rename succeeds, so the page reloads the tree. */
|
|
97
|
+
onRenamed: () => void;
|
|
98
|
+
/** Called after a delete-to-trash succeeds, so the page reloads the tree. */
|
|
99
|
+
onDeleted: () => void;
|
|
100
|
+
/** What the clipboard currently holds, or null when it is empty. */
|
|
101
|
+
clipboard: TreeClipboard | null;
|
|
102
|
+
/** Put one entry on the clipboard. Recording only; nothing moves until paste. */
|
|
103
|
+
onClipboard: (entry: TreeClipboard | null) => void;
|
|
104
|
+
/**
|
|
105
|
+
* The directory a paste would land in.
|
|
106
|
+
*
|
|
107
|
+
* For a directory row the target is that directory; for a file it is the file's
|
|
108
|
+
* parent. The menu states it so a paste never lands somewhere the reader cannot see,
|
|
109
|
+
* and `null` means the destination is unknown, which disables the entry rather than
|
|
110
|
+
* guessing.
|
|
111
|
+
*/
|
|
112
|
+
pasteInto: string | null;
|
|
113
|
+
/** Called after a paste succeeds, so the page reloads both affected levels. */
|
|
114
|
+
onMoved: () => void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The knowledge tree's row context menu.
|
|
118
|
+
* @param props - the target row, the pointer position, and the actions.
|
|
119
|
+
* @returns The menu, or nothing when it is closed.
|
|
120
|
+
*/
|
|
121
|
+
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,117 @@
|
|
|
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
|
+
* Whether one path is currently favourited, so its row can draw the star.
|
|
84
|
+
*
|
|
85
|
+
* The borrowed explorer has no row-decoration hook, so the star is applied by this
|
|
86
|
+
* wrapper observing the rows it renders (see `useRowFavorites`). Absent means the
|
|
87
|
+
* caller has no favourites store, which keeps the wrapper from touching the DOM at all.
|
|
88
|
+
*/
|
|
89
|
+
isFavorite?: (relPath: string) => boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Open the row context menu for one row.
|
|
92
|
+
*
|
|
93
|
+
* The explorer builds its own menu inside itself and marks the event handled, so the
|
|
94
|
+
* only place to intercept is a capture-phase listener above it. This wrapper does
|
|
95
|
+
* that and forwards the row here; the page then renders the SAME menu component the
|
|
96
|
+
* home table uses, so both surfaces offer one action set.
|
|
97
|
+
*
|
|
98
|
+
* Absent means "leave the explorer's own menu alone", which is what a read-only
|
|
99
|
+
* surface embedding this tree would want.
|
|
100
|
+
*/
|
|
101
|
+
onRowContextMenu?: (relPath: string, isDir: boolean, at: {
|
|
102
|
+
x: number;
|
|
103
|
+
y: number;
|
|
104
|
+
}, selection?: readonly {
|
|
105
|
+
relPath: string;
|
|
106
|
+
name: string;
|
|
107
|
+
isDir: boolean;
|
|
108
|
+
}[]) => void;
|
|
109
|
+
/** The favourites star's accessible name. */
|
|
110
|
+
favoriteMarkLabel?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The knowledge tree, rendered by the product's file explorer.
|
|
114
|
+
* @param props - the root, the reader's expansion state, and the actions.
|
|
115
|
+
* @returns The explorer's tree, rooted at the knowledge directory.
|
|
116
|
+
*/
|
|
117
|
+
export declare function KnowledgeFileTree(props: KnowledgeFileTreeProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
* Upload files dropped onto the table area.
|
|
75
|
+
*
|
|
76
|
+
* The table lists documents only -- there is no directory row here to aim at -- so
|
|
77
|
+
* the whole surface is one target and the destination has to be decided rather than
|
|
78
|
+
* read off the row under the pointer. `dropDirectory` carries that decision.
|
|
79
|
+
* Omitted by a read-only caller, which keeps the surface from accepting a drop that
|
|
80
|
+
* would then be refused on the Host.
|
|
81
|
+
* @param directory - the tree-relative directory the files land in.
|
|
82
|
+
* @param files - the dropped files with paths relative to that directory.
|
|
83
|
+
*/
|
|
84
|
+
onUpload?: (directory: string, files: readonly {
|
|
85
|
+
file: unknown;
|
|
86
|
+
relativePath: string;
|
|
87
|
+
}[]) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Where a drop on this table lands, tree-relative.
|
|
90
|
+
*
|
|
91
|
+
* The reader's own folder is the right default: `global` is the bucket everyone
|
|
92
|
+
* reads, so defaulting there would publish a private draft without asking. Empty
|
|
93
|
+
* means the mount root, which is what a caller with no personal bucket gets.
|
|
94
|
+
*/
|
|
95
|
+
dropDirectory?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Render the Home surface.
|
|
99
|
+
* @param props - copy resolver, mount state, and the open callback.
|
|
100
|
+
* @returns The Home view.
|
|
101
|
+
*/
|
|
102
|
+
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;
|