@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,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The knowledge base's search: find a document, then open it.
|
|
3
|
+
*
|
|
4
|
+
* The field itself lives in the page's rail, not here, because it is navigation:
|
|
5
|
+
* a reader reaches "find a document" the same way they reach the permission or
|
|
6
|
+
* indexing section, and a menu row labelled "Search" asked them to click through
|
|
7
|
+
* to a box before they could type. `useKnowledgeSearch` owns the one piece of
|
|
8
|
+
* state both halves need, so the rail's field and this result list cannot
|
|
9
|
+
* disagree about which term produced which hits.
|
|
10
|
+
*
|
|
11
|
+
* This replaces an earlier summary of mount state, which answered a question the
|
|
12
|
+
* operator had not asked: the mount path, the file count, and a sync timestamp are
|
|
13
|
+
* facts about the deployment, while the reason to open this page is to locate a
|
|
14
|
+
* document.
|
|
15
|
+
*
|
|
16
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
17
|
+
*/
|
|
18
|
+
/** One result row, as the Host returns it. */
|
|
19
|
+
interface SearchHit {
|
|
20
|
+
/** Path relative to the mount point. */
|
|
21
|
+
relPath: string;
|
|
22
|
+
/** File name alone. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Whether the term matched the file body rather than only its name. */
|
|
25
|
+
inContent: boolean;
|
|
26
|
+
/** Matching line, when the term was found inside the file. */
|
|
27
|
+
line?: string;
|
|
28
|
+
/** 1-based line number of {@link line}. */
|
|
29
|
+
lineNumber?: number;
|
|
30
|
+
/** Last-modified instant in Unix ms, when the Host could stat the file. */
|
|
31
|
+
mtimeMs?: number;
|
|
32
|
+
}
|
|
33
|
+
/** What {@link useKnowledgeSearch} hands back to the rail and the result list. */
|
|
34
|
+
export interface KnowledgeSearchState {
|
|
35
|
+
/** Current term, owned here so the field and the results read one value. */
|
|
36
|
+
term: string;
|
|
37
|
+
/** Replace the term. */
|
|
38
|
+
setTerm: (value: string) => void;
|
|
39
|
+
/** Hits for the settled term, empty while idle. */
|
|
40
|
+
hits: SearchHit[];
|
|
41
|
+
/** Which surface the result list should draw. */
|
|
42
|
+
status: 'idle' | 'searching' | 'done' | 'error';
|
|
43
|
+
/** Index of the keyboard-highlighted row. */
|
|
44
|
+
active: number;
|
|
45
|
+
/** Highlight a row, so pointer movement and the arrow keys agree. */
|
|
46
|
+
setActive: (index: number) => void;
|
|
47
|
+
/** Arrow-key handling for the rail's field. */
|
|
48
|
+
onKeyDown: (event: {
|
|
49
|
+
key: string;
|
|
50
|
+
preventDefault: () => void;
|
|
51
|
+
}) => void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Own the search term, its debounced request, and the highlighted row.
|
|
55
|
+
*
|
|
56
|
+
* Extracted rather than left inside the result component because the field it is
|
|
57
|
+
* driven by now sits in the rail: a hook keeps one owner for the term while the
|
|
58
|
+
* input and the list render in different regions of the page.
|
|
59
|
+
*
|
|
60
|
+
* @param mountPoint - the mount to search; empty while the page is still loading.
|
|
61
|
+
* @returns The term, its hits, and the keyboard handler for the field.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useKnowledgeSearch(mountPoint: string): KnowledgeSearchState;
|
|
64
|
+
/** Props for the result list. */
|
|
65
|
+
export interface KnowledgeSearchProps {
|
|
66
|
+
/** Locale-bound copy resolver. */
|
|
67
|
+
t: (key: string) => string;
|
|
68
|
+
/** Term and hits, owned by {@link useKnowledgeSearch}. */
|
|
69
|
+
search: KnowledgeSearchState;
|
|
70
|
+
/** Called with the chosen path so the page can show its preview. */
|
|
71
|
+
onOpen: (path: string) => void;
|
|
72
|
+
/** Locale tag, used to render dates and relative spans in the reader's language. */
|
|
73
|
+
locale?: string;
|
|
74
|
+
/** Shown while no term is entered, so the surface is useful before searching. */
|
|
75
|
+
idle?: React.ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Render the results for the rail's search field.
|
|
79
|
+
* @param props - copy resolver, the search state, and the open callback.
|
|
80
|
+
* @returns The result list, or the idle content when nothing is typed.
|
|
81
|
+
*/
|
|
82
|
+
export declare function KnowledgeSearch(props: KnowledgeSearchProps): import("react").JSX.Element;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rail's section bands: a name with an optional trailing glyph cluster.
|
|
3
|
+
*
|
|
4
|
+
* Copied from the shell's own browser header,
|
|
5
|
+
* `ui-workspace/src/client/rows/WorkspaceBrowser.module.css` (`.sectionHeader` /
|
|
6
|
+
* `.sectionLabel` + `.iconButton`). That band is the one a reader compares this
|
|
7
|
+
* page against, because it is what the right sidebar shows for 工作区 in the same
|
|
8
|
+
* window. The metrics were also measured live on the running shell on 2026-10-17
|
|
9
|
+
* and agree with the stylesheet exactly: a 36px row, `gap: 4px`,
|
|
10
|
+
* `padding-left: 4px`, a label at 14px/20px, and 28x28 buttons on an 8px radius.
|
|
11
|
+
*
|
|
12
|
+
* Two details are the stylesheet's own and easy to get wrong:
|
|
13
|
+
*
|
|
14
|
+
* - The label is *not* bold. `--dsw-font-s-14` is 14px/22px, while this band's
|
|
15
|
+
* line-height is an explicit 20px, so the shorthand is paired with the measured
|
|
16
|
+
* line-height rather than allowed to win.
|
|
17
|
+
* - The name inherits the band's ink, which the stylesheet sets to
|
|
18
|
+
* `--dsw-alias-label-tertiary`; the buttons carry
|
|
19
|
+
* `--dsw-alias-label-secondary` of their own. Measured live, the name is
|
|
20
|
+
* `#adb2b8` and the buttons `#cfd3d6`, which is that same split.
|
|
21
|
+
*
|
|
22
|
+
* The band is a plain `div` with a name span and a button cluster rather than a
|
|
23
|
+
* heading element: it labels a region that already carries its own landmark, and
|
|
24
|
+
* a second heading level here would put an unlabelled h3 in the heading outline.
|
|
25
|
+
*
|
|
26
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
27
|
+
*/
|
|
28
|
+
import type { ReactNode } from 'react';
|
|
29
|
+
/** Props for one section band. */
|
|
30
|
+
export interface KnowledgeSectionHeaderProps {
|
|
31
|
+
/** Localized section name, rendered at the measured 14px/20px. */
|
|
32
|
+
name: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* The trailing controls.
|
|
35
|
+
*
|
|
36
|
+
* Optional because the pinned band's only control is the fold toggle, while
|
|
37
|
+
* the tree band carries three; a band with none renders the name alone.
|
|
38
|
+
*/
|
|
39
|
+
actions?: ReactNode;
|
|
40
|
+
/** Extra class for the band, so a caller can add its own spacing. */
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render one section band.
|
|
45
|
+
* @param props - the name, its trailing controls, and optional placement.
|
|
46
|
+
* @returns The band.
|
|
47
|
+
*/
|
|
48
|
+
export declare function KnowledgeSectionHeader(props: KnowledgeSectionHeaderProps): import("react").JSX.Element;
|
|
49
|
+
/** Props for one 28px glyph button in a band's cluster. */
|
|
50
|
+
export interface KnowledgeIconButtonProps {
|
|
51
|
+
/** Localized accessible name; also the tooltip, as the shell's own buttons do. */
|
|
52
|
+
label: string;
|
|
53
|
+
/** The glyph, sized by the caller to the band's 16px step. */
|
|
54
|
+
children: ReactNode;
|
|
55
|
+
/** Invoked on activation. */
|
|
56
|
+
onClick: () => void;
|
|
57
|
+
/** Whether the control refuses activation. */
|
|
58
|
+
disabled?: boolean;
|
|
59
|
+
/** Whether the control's action is currently engaged, so it can report `aria-pressed`. */
|
|
60
|
+
pressed?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Render one glyph button.
|
|
64
|
+
*
|
|
65
|
+
* A native button with the band's own class rather than the `Button` primitive:
|
|
66
|
+
* that primitive is the product's labelled action at a 36px control height, while
|
|
67
|
+
* this is the 28x28 icon control the band is specified around.
|
|
68
|
+
* @param props - the accessible name, glyph, and activation handler.
|
|
69
|
+
* @returns The button.
|
|
70
|
+
*/
|
|
71
|
+
export declare function KnowledgeIconButton(props: KnowledgeIconButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The batch action bar that closes a multi-select session in the tree.
|
|
3
|
+
*
|
|
4
|
+
* Copied from the explorer's own `explorerSelectionBar` (`FileTree.tsx:2238`,
|
|
5
|
+
* `sidebar.module.css:932`): the same `role="toolbar"`, the same sticky bottom
|
|
6
|
+
* position, the same 28px minimum height, the same `bg-layer-2` fill and 4px
|
|
7
|
+
* radius, and the same split between a shrinking count on the left and the
|
|
8
|
+
* actions on the right. It is a copy rather than a second design because a reader
|
|
9
|
+
* sees that bar in this window's right sidebar, and the two are the same gesture.
|
|
10
|
+
*
|
|
11
|
+
* The actions are deliberately only the two this build can actually perform.
|
|
12
|
+
* Delete is absent because the trash has no decided retention or ownership
|
|
13
|
+
* semantics, and a destructive button wired to an undecided policy is worse than
|
|
14
|
+
* no button: the explorer's own bar carries delete, so its absence here is a
|
|
15
|
+
* decision rather than an oversight.
|
|
16
|
+
*
|
|
17
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
18
|
+
*/
|
|
19
|
+
/** Props for the batch bar. */
|
|
20
|
+
export interface KnowledgeSelectionBarProps {
|
|
21
|
+
/** Locale-bound copy resolver. */
|
|
22
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
23
|
+
/** How many rows are selected, for the count and the label. */
|
|
24
|
+
count: number;
|
|
25
|
+
/** Move every selected path into one folder the reader picks. */
|
|
26
|
+
onMove: () => void;
|
|
27
|
+
/** Pin every selected path. */
|
|
28
|
+
onPin: () => void;
|
|
29
|
+
/** Drop the selection. */
|
|
30
|
+
onClear: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Whether a batch write is in flight, so the actions can be held.
|
|
33
|
+
*
|
|
34
|
+
* A second click while the first is still moving would send the same paths twice,
|
|
35
|
+
* and the second would be refused as a missing source -- an error the reader
|
|
36
|
+
* would read as the feature being broken.
|
|
37
|
+
*/
|
|
38
|
+
busy?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Render the batch bar for the current selection.
|
|
42
|
+
* @param props - the count, the three actions, and the busy flag.
|
|
43
|
+
* @returns The toolbar.
|
|
44
|
+
*/
|
|
45
|
+
export declare function KnowledgeSelectionBar(props: KnowledgeSelectionBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO: this page is unreachable from the real entry, and needs a decision.
|
|
3
|
+
*
|
|
4
|
+
* Verified by driving the deployed product on 2026-10-21, not by reading the code:
|
|
5
|
+
* entering 知识库 and clicking 「设置」 in its own rail opens the DSH GLOBAL settings
|
|
6
|
+
* dialog (left nav: 通用设置/模型/内置插件/Agent 预设/DataOps/备份/知识库/插件市场/
|
|
7
|
+
* 侧边卡片), never this component. The rail row sets `selection.kind = 'settings'`,
|
|
8
|
+
* which `KnowledgePanel.tsx` renders as this surface, but the click that a reader can
|
|
9
|
+
* actually reach is the shell's own settings entry, so nothing in the real UI leads
|
|
10
|
+
* here. The review switch below therefore has no user-facing effect today.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately NOT deleted: removing this file, the `rendered.kind === 'settings'`
|
|
13
|
+
* branch in `KnowledgePanel.tsx` and the `menu.settings` rail row is a cross-file
|
|
14
|
+
* structural change, and the choice between "delete the page" and "give it a real
|
|
15
|
+
* entry point" is a product decision rather than a cleanup. Left as-is with this note
|
|
16
|
+
* so the next change starts from the measured fact instead of re-deriving it.
|
|
17
|
+
*
|
|
18
|
+
* What WAS moved: the MinerU service address. It lived here for one round and was
|
|
19
|
+
* invisible to readers for exactly this reason; it now renders in
|
|
20
|
+
* `KnowledgeSettingsSection.tsx` (DSH global settings → 知识库), which is reachable.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The settings surface of the management page.
|
|
24
|
+
*
|
|
25
|
+
* This page is now only the review switch: a DECISION about whether other people's
|
|
26
|
+
* contributions take effect immediately, so it is a control and it needs write
|
|
27
|
+
* access. The queue that decision governs and the trail of what it settled both
|
|
28
|
+
* moved to the Audit page, which is the surface an operator opens to get through
|
|
29
|
+
* work; parking a worklist under a configuration switch made them scroll past the
|
|
30
|
+
* setting to reach today's arrivals, and left two duplicate entry points pointing
|
|
31
|
+
* at the same two tables.
|
|
32
|
+
*
|
|
33
|
+
* The switch ships OFF. That is the product decision, not a default that drifted: an
|
|
34
|
+
* operator who never opens this page keeps the behaviour they had, and turning review
|
|
35
|
+
* on is a deliberate act with a visible consequence.
|
|
36
|
+
*
|
|
37
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
38
|
+
*/
|
|
39
|
+
/** This knowledge base's review settings, as the Host reports them. */
|
|
40
|
+
export interface KnowledgeSettingsView {
|
|
41
|
+
/** Whether a write must be reviewed before it counts as published. */
|
|
42
|
+
reviewRequired: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* MinerU's full base address, or `''` to derive it from the configured port.
|
|
45
|
+
*
|
|
46
|
+
* Absent from an older Host's answer, which reads as `''`: the field then shows
|
|
47
|
+
* "derived" rather than a value the Host never sent.
|
|
48
|
+
*/
|
|
49
|
+
mineruBaseUrl?: string;
|
|
50
|
+
}
|
|
51
|
+
/** One recorded change, as the Host reports it. */
|
|
52
|
+
export interface KnowledgeAuditEntryView {
|
|
53
|
+
/** ISO timestamp of the action. */
|
|
54
|
+
at: string;
|
|
55
|
+
/** DataOps user id that performed it: the identity the trail is checked against. */
|
|
56
|
+
actor: string;
|
|
57
|
+
/**
|
|
58
|
+
* That user's display name, when the entry carries one.
|
|
59
|
+
*
|
|
60
|
+
* Absent on entries written before it was recorded; the column falls back to the
|
|
61
|
+
* id so an old line still identifies its actor.
|
|
62
|
+
*/
|
|
63
|
+
actorLabel?: string;
|
|
64
|
+
/** What they did. Mirrors the Host's own action union. */
|
|
65
|
+
action: 'create' | 'upload' | 'move' | 'approve' | 'reject' | 'edit';
|
|
66
|
+
/** The path the action concerned. */
|
|
67
|
+
relPath: string;
|
|
68
|
+
/** For a move, the destination path. */
|
|
69
|
+
toRelPath?: string;
|
|
70
|
+
/** Whether it took effect immediately or was parked for review or refused. */
|
|
71
|
+
outcome: 'applied' | 'pending' | 'rejected';
|
|
72
|
+
}
|
|
73
|
+
/** Props for the settings surface. */
|
|
74
|
+
export interface KnowledgeSettingsProps {
|
|
75
|
+
/** Locale-bound copy for this package's namespace. */
|
|
76
|
+
t: (key: string) => string;
|
|
77
|
+
}
|
|
78
|
+
/** One submission waiting for review, as the Host reports it. */
|
|
79
|
+
export interface PendingWriteView {
|
|
80
|
+
/** The parking name a decision is made against. */
|
|
81
|
+
name: string;
|
|
82
|
+
/** Who submitted it. */
|
|
83
|
+
userId: string;
|
|
84
|
+
/** The path it wants to occupy. */
|
|
85
|
+
relPath: string;
|
|
86
|
+
/** When it was submitted. */
|
|
87
|
+
at: string;
|
|
88
|
+
/** Size of the submitted bytes. */
|
|
89
|
+
bytes: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Render the review switch and the audit trail.
|
|
93
|
+
* @param props - copy resolver.
|
|
94
|
+
* @returns The settings surface.
|
|
95
|
+
*/
|
|
96
|
+
export declare function KnowledgeSettings(props: KnowledgeSettingsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { en } from './locales.ts';
|
|
3
|
+
/** Values injected by the DSH Settings slot. */
|
|
4
|
+
export interface KnowledgeSettingsInjected {
|
|
5
|
+
/** Translate one knowledge-base message key. */
|
|
6
|
+
t: (key: keyof typeof en) => string;
|
|
7
|
+
}
|
|
8
|
+
/** Props accepted by the injected Settings section. */
|
|
9
|
+
export type KnowledgeSettingsProps = Partial<InjectFace<KnowledgeSettingsInjected>>;
|
|
10
|
+
/**
|
|
11
|
+
* Render the knowledge-base connection, resolved paths, status, the read-only
|
|
12
|
+
* authorization summary, and the deployment wizard.
|
|
13
|
+
*
|
|
14
|
+
* Authorization itself is deliberately absent: DataOps owns grants, and this
|
|
15
|
+
* panel only reports what the backend says (AGENTS.md "Managed DataOps
|
|
16
|
+
* Authorization" forbids replicating that policy in a plugin).
|
|
17
|
+
*
|
|
18
|
+
* @param props - Settings slot injection values.
|
|
19
|
+
* @returns The localized Settings section, or nothing before injection.
|
|
20
|
+
*/
|
|
21
|
+
export declare function KnowledgeSettingsSection(props: KnowledgeSettingsProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Share dialog the knowledge tree's context menu opens.
|
|
3
|
+
*
|
|
4
|
+
* Sharing is a grant, and the grant route already exists: this dialog only chooses
|
|
5
|
+
* WHO receives it. The principal directory behind the picker is the same one the
|
|
6
|
+
* permission table reads, reached through the same `principals` route, so a reader
|
|
7
|
+
* who can grant from either surface picks from one list rather than two that can
|
|
8
|
+
* disagree.
|
|
9
|
+
*
|
|
10
|
+
* The picker is a text field plus a filtered list rather than a `<select>`: a real
|
|
11
|
+
* deployment has hundreds of people, and a closed control makes finding one a
|
|
12
|
+
* scroll. Filtering is local over the directory the Host already returned, so
|
|
13
|
+
* typing costs no round trip and stays responsive while a slow directory is still
|
|
14
|
+
* loading.
|
|
15
|
+
*
|
|
16
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
17
|
+
*/
|
|
18
|
+
/** One selectable principal, as the Host's directory reports it. */
|
|
19
|
+
export interface SharePrincipal {
|
|
20
|
+
/** Stable id the grant names. */
|
|
21
|
+
id: string;
|
|
22
|
+
/** What a reader recognises it by: a person's name, a role's title. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** Optional second line: department, email, or whatever distinguishes two like-named ids. */
|
|
25
|
+
detail?: string;
|
|
26
|
+
}
|
|
27
|
+
/** The copy this dialog needs, so it renders in the reader's language. */
|
|
28
|
+
export interface ShareDialogCopy {
|
|
29
|
+
title: string;
|
|
30
|
+
intro: string;
|
|
31
|
+
subjectType: string;
|
|
32
|
+
search: string;
|
|
33
|
+
searchPlaceholder: string;
|
|
34
|
+
noMatch: string;
|
|
35
|
+
loading: string;
|
|
36
|
+
empty: string;
|
|
37
|
+
cancel: string;
|
|
38
|
+
confirm: string;
|
|
39
|
+
working: string;
|
|
40
|
+
permission: string;
|
|
41
|
+
actionRead: string;
|
|
42
|
+
actionWrite: string;
|
|
43
|
+
actionManage: string;
|
|
44
|
+
chosen: string;
|
|
45
|
+
failed: string;
|
|
46
|
+
directoryFailed: string;
|
|
47
|
+
kind_user: string;
|
|
48
|
+
kind_role: string;
|
|
49
|
+
kind_department: string;
|
|
50
|
+
kind_group: string;
|
|
51
|
+
}
|
|
52
|
+
/** Props for the share dialog. */
|
|
53
|
+
export interface KnowledgeShareDialogProps {
|
|
54
|
+
/** The path being shared, relative to the knowledge root, or null when closed. */
|
|
55
|
+
relPath: string | null;
|
|
56
|
+
/** Whether that path is a directory; only used to word the intro. */
|
|
57
|
+
isDir: boolean;
|
|
58
|
+
/** Locale-bound copy for this dialog. */
|
|
59
|
+
copy: ShareDialogCopy;
|
|
60
|
+
/** Close the dialog. */
|
|
61
|
+
onClose: () => void;
|
|
62
|
+
/** Called after a grant landed, so the page can refresh its permission view. */
|
|
63
|
+
onShared?: () => void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The Share dialog.
|
|
67
|
+
* @param props - the target path, the copy, and the callbacks.
|
|
68
|
+
* @returns The modal, or nothing while it is closed.
|
|
69
|
+
*/
|
|
70
|
+
export declare function KnowledgeShareDialog(props: KnowledgeShareDialogProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The template library: a card grid in an official dialog, with a maintenance side.
|
|
3
|
+
*
|
|
4
|
+
* A template is a document plus the few facts that describe it -- name, one-line
|
|
5
|
+
* description, category, optional tags. That is the whole model, and it is why the
|
|
6
|
+
* store is a directory of documents next to one JSON index rather than a table: the
|
|
7
|
+
* file IS the template, and the index only says what to call it.
|
|
8
|
+
*
|
|
9
|
+
* Two jobs live here, because they are two readings of the same list:
|
|
10
|
+
*
|
|
11
|
+
* - USING a template. A card's primary action copies the template's document into a
|
|
12
|
+
* directory the reader picks, through the same upload route any new document uses,
|
|
13
|
+
* then opens it. Nothing about a template is special enough to need its own write
|
|
14
|
+
* path.
|
|
15
|
+
* - MAINTAINING the list. Adding a template is "choose a document, fill in the
|
|
16
|
+
* metadata"; editing is the same form with the file already stored; taking one
|
|
17
|
+
* offline removes the record and its document.
|
|
18
|
+
*
|
|
19
|
+
* The empty states are the honest ones: "nothing stored yet" and "could not read the
|
|
20
|
+
* store" are different facts and say so separately, because a reader told the library
|
|
21
|
+
* is empty will believe it.
|
|
22
|
+
*
|
|
23
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
24
|
+
*/
|
|
25
|
+
/** Props for the template library. */
|
|
26
|
+
export interface KnowledgeTemplatesProps {
|
|
27
|
+
/** Locale-bound copy resolver. */
|
|
28
|
+
t: (key: string) => string;
|
|
29
|
+
/** Close the dialog. */
|
|
30
|
+
onClose: () => void;
|
|
31
|
+
/** The knowledge root, for the "use this template" destination default. */
|
|
32
|
+
mountPoint?: string;
|
|
33
|
+
/** The caller's id, so the destination default prefers their own folder. */
|
|
34
|
+
callerId?: string;
|
|
35
|
+
/** Open a document once it has been created from a template. */
|
|
36
|
+
onOpen?: (relPath: string) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Render the template library.
|
|
40
|
+
* @param props - copy resolver, the knowledge root, and the close handler.
|
|
41
|
+
* @returns The dialog.
|
|
42
|
+
*/
|
|
43
|
+
export declare function KnowledgeTemplates(props: KnowledgeTemplatesProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Trash surface: what this caller removed, and the way back.
|
|
3
|
+
*
|
|
4
|
+
* ## What a removal is here
|
|
5
|
+
*
|
|
6
|
+
* Removing a document does not delete it. A view is hardlinks into one shared tree,
|
|
7
|
+
* so unlinking the caller's own link would be indistinguishable from a revoked grant
|
|
8
|
+
* and the next sync would put the link back. The removal is therefore recorded as a
|
|
9
|
+
* tombstone that filters this caller's view, and restoring deletes the note. See
|
|
10
|
+
* `src/trash.ts` for the full reasoning.
|
|
11
|
+
*
|
|
12
|
+
* ## Why restore can bring back nothing
|
|
13
|
+
*
|
|
14
|
+
* A grant can be withdrawn while a document sits in the trash. Restoring it then
|
|
15
|
+
* rebuilds the view without that document, which is the correct outcome and one the
|
|
16
|
+
* page has to say out loud -- a Restore button that appears to do nothing reads as a
|
|
17
|
+
* broken page.
|
|
18
|
+
*
|
|
19
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
20
|
+
*/
|
|
21
|
+
/** Props for the Trash surface. */
|
|
22
|
+
export interface KnowledgeTrashProps {
|
|
23
|
+
/** Locale-bound copy resolver. */
|
|
24
|
+
t: (key: string) => string;
|
|
25
|
+
}
|
|
26
|
+
/** One removed path, as the route reports it. */
|
|
27
|
+
interface TrashEntry {
|
|
28
|
+
/** Path relative to the knowledge root, in view coordinates. */
|
|
29
|
+
relPath: string;
|
|
30
|
+
/** Whether the removed entry was a file or a directory. */
|
|
31
|
+
kind: 'file' | 'directory';
|
|
32
|
+
/** Display name of whoever removed it. */
|
|
33
|
+
removedBy: string;
|
|
34
|
+
/** ISO-8601 instant of removal. */
|
|
35
|
+
removedAt: string;
|
|
36
|
+
/** Where it used to sit. */
|
|
37
|
+
originalPath: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Format one removal instant for a row.
|
|
41
|
+
*
|
|
42
|
+
* @param value - ISO-8601 instant from the route.
|
|
43
|
+
* @param locale - BCP-47 tag the browser should format in.
|
|
44
|
+
* @returns the local date and time, or the raw value when it cannot be read.
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatRemovedAt(value: string, locale?: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* The last segment of a view path, which is the name a reader recognises.
|
|
49
|
+
*
|
|
50
|
+
* @param relPath - path in view coordinates.
|
|
51
|
+
* @returns that segment, or the whole path when it holds no separator.
|
|
52
|
+
*/
|
|
53
|
+
export declare function entryNameOf(relPath: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Render the Trash surface.
|
|
56
|
+
*
|
|
57
|
+
* @param props - copy resolver.
|
|
58
|
+
* @returns The trash table, its empty state, and a restore action per row.
|
|
59
|
+
*/
|
|
60
|
+
export declare function KnowledgeTrash(props: KnowledgeTrashProps): import("react").JSX.Element;
|
|
61
|
+
/** One removed path, re-exported for callers that build their own table. */
|
|
62
|
+
export type { TrashEntry };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glyphs for the management page's own menu rows.
|
|
3
|
+
*
|
|
4
|
+
* These are the product's own icon components, not hand-drawn paths: the icon set
|
|
5
|
+
* already draws each glyph on the product grid with the product stroke weights
|
|
6
|
+
* (`ICON_REGULAR_STROKE` 1, `ICON_MEDIUM_STROKE` 1.3), and a local SVG with its own
|
|
7
|
+
* stroke reads noticeably heavier beside them. Colour rides `currentColor`, so a
|
|
8
|
+
* row's own text colour drives the glyph and no row needs an icon-specific rule.
|
|
9
|
+
*
|
|
10
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
11
|
+
*/
|
|
12
|
+
/** One rail section id, matching a selection kind. */
|
|
13
|
+
export type SectionKind = 'home' | 'permissions' | 'indexing' | 'trash' | 'audit' | 'settings';
|
|
14
|
+
/**
|
|
15
|
+
* The glyph matching one rail section.
|
|
16
|
+
*
|
|
17
|
+
* Kept as a map rather than a switch so the menu can render a row per entry and
|
|
18
|
+
* stay a single expression. The document glyph stands for Home because that is
|
|
19
|
+
* what the surface holds, and it is the same mark the left sidebar uses for this
|
|
20
|
+
* plugin, so the entry reads consistently in both rails.
|
|
21
|
+
* @param kind - section id, matching the selection kind.
|
|
22
|
+
* @param size - glyph size.
|
|
23
|
+
* @returns The glyph element.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sectionGlyph(kind: SectionKind, size?: number): import("react").JSX.Element;
|
|
26
|
+
/**
|
|
27
|
+
* The brand mark at the rail's top-left.
|
|
28
|
+
*
|
|
29
|
+
* The same glyph the left sidebar shows for this plugin, at the shell's menu-icon
|
|
30
|
+
* size rather than a rail-sized one: this row is a page header, and the sidebar's
|
|
31
|
+
* own larger step belongs to the global rail rather than to a page inside it.
|
|
32
|
+
* @param props - whether the rail is folded, which drops the mark to icon size.
|
|
33
|
+
* @returns The brand glyph.
|
|
34
|
+
*/
|
|
35
|
+
export declare function KnowledgeMenuBrand(props: {
|
|
36
|
+
folded: boolean;
|
|
37
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordering for the audit trail.
|
|
3
|
+
*
|
|
4
|
+
* Only the ordering lives here. Paging is the shared `paging.ts` utility that the other
|
|
5
|
+
* tables in this package use, adopted rather than duplicated: two implementations of
|
|
6
|
+
* "slice a page and clamp it" drift, and the one that is not used by the table next door
|
|
7
|
+
* is the one that gets the fix.
|
|
8
|
+
*
|
|
9
|
+
* The order is a correctness rule, not a presentation detail. The Host happens to append
|
|
10
|
+
* newest-last to its log, so a table that drew the array as received would look right
|
|
11
|
+
* until the writer changed. The reader's question is "what happened most recently", and
|
|
12
|
+
* that answer is computed here from the timestamps.
|
|
13
|
+
*
|
|
14
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
15
|
+
*/
|
|
16
|
+
/** The fields the ordering rule needs. */
|
|
17
|
+
export interface AuditRow {
|
|
18
|
+
/** ISO-8601 instant the row records. */
|
|
19
|
+
at: string;
|
|
20
|
+
/** Path the action concerned, used as the tie-breaker. */
|
|
21
|
+
relPath: string;
|
|
22
|
+
/** What was done, used as the second tie-breaker. */
|
|
23
|
+
action: string;
|
|
24
|
+
/** Destination for a move, used as the last tie-breaker. */
|
|
25
|
+
toRelPath?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Order rows newest first, with a stable tie-break.
|
|
29
|
+
*
|
|
30
|
+
* The secondary keys are what stop rows from swapping places between renders when several
|
|
31
|
+
* share an instant -- the normal case for a batch, where one action writes several rows
|
|
32
|
+
* with the same `at`. Without them the platform sort could order equal keys either way,
|
|
33
|
+
* and a reader paging forward would see a row twice or not at all.
|
|
34
|
+
*
|
|
35
|
+
* Ordering is total: `relPath`, then `action`, then `toRelPath` are all compared, and two
|
|
36
|
+
* rows equal in every one of those are indistinguishable to this table anyway.
|
|
37
|
+
* @param rows - the rows as received.
|
|
38
|
+
* @returns A new array; the input is not mutated.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sortAuditRows<T extends AuditRow>(rows: readonly T[]): T[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure half of the tree's folder labels: which rows print a name instead of a slug.
|
|
3
|
+
*
|
|
4
|
+
* Kept apart from the rendering module for the reason stated in `permissionRows.ts`:
|
|
5
|
+
* a component module reaches the primitives package, which imports CSS, and a Node test
|
|
6
|
+
* runner cannot load a `.css` file. The label rules are the part worth testing without a
|
|
7
|
+
* DOM, so they live where a plain import can reach them.
|
|
8
|
+
*
|
|
9
|
+
* @module @sparkelf/dsh-knowledge-base/client/departmentFolders
|
|
10
|
+
*/
|
|
11
|
+
/** The reader's own organization chain, as the Host reports it. */
|
|
12
|
+
export interface DepartmentPath {
|
|
13
|
+
/** Codes from the organization root down to the caller's department. */
|
|
14
|
+
codes: readonly string[];
|
|
15
|
+
/** Display names, index-aligned with {@link codes}. */
|
|
16
|
+
names: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tree-relative paths of the caller's own department chain, mapped to their names.
|
|
20
|
+
*
|
|
21
|
+
* The department bucket stores folders by code (`nanning-psb`), because a code is a
|
|
22
|
+
* stable slug: renaming a department cannot move a document. The reader, though, knows
|
|
23
|
+
* their organization by name, so those same rows have to print 「南宁市公安局」 rather
|
|
24
|
+
* than the slug.
|
|
25
|
+
*
|
|
26
|
+
* One entry per LEVEL rather than one for the leaf: the reader walks
|
|
27
|
+
* 「部门」 → 「南宁市公安局」 → 「数字侦察支队」 → 「数据实战大队」, so each of those
|
|
28
|
+
* three rows needs its own name.
|
|
29
|
+
*
|
|
30
|
+
* Only the caller's own chain is translated. A peer department's folder keeps printing
|
|
31
|
+
* its code, because this build has no name for it: the whole organization tree is
|
|
32
|
+
* readable only by an administrator, and deriving a "name" from a slug would put an
|
|
33
|
+
* invented label in front of the reader.
|
|
34
|
+
*
|
|
35
|
+
* A mismatched pair is refused whole rather than zipped to the shorter length: a
|
|
36
|
+
* partially labelled chain would put a correct name on one row and a raw code on the
|
|
37
|
+
* next, which reads as a missing department rather than as a missing translation.
|
|
38
|
+
*
|
|
39
|
+
* @param codes - codes from the organization root down, as DataOps reported them.
|
|
40
|
+
* @param names - display names, index-aligned.
|
|
41
|
+
* @returns The lookup keyed by tree-relative path, or undefined when nothing is usable.
|
|
42
|
+
*/
|
|
43
|
+
export declare function departmentPathLabels(codes: readonly string[] | undefined, names: readonly string[] | undefined): ReadonlyMap<string, string> | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve one tree-relative path to its printed label.
|
|
46
|
+
*
|
|
47
|
+
* Bucket labels are consulted first. The two maps address different rows today
|
|
48
|
+
* (`department` for the bucket, `department/<code>` and deeper for the chain), so the
|
|
49
|
+
* order changes no current label; it states that the audience name of a bucket is the
|
|
50
|
+
* one thing a data value must not override.
|
|
51
|
+
*
|
|
52
|
+
* @param relPath - path relative to the knowledge root.
|
|
53
|
+
* @param bucketLabels - the three audience names, keyed the same way.
|
|
54
|
+
* @param chainLabels - the caller's department chain, from {@link departmentPathLabels}.
|
|
55
|
+
* @returns The label to print, or undefined to print the row's own name.
|
|
56
|
+
*/
|
|
57
|
+
export declare function folderLabelOf(relPath: string, bucketLabels: Readonly<Record<string, string>> | undefined, chainLabels: ReadonlyMap<string, string> | undefined): string | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a deployment has anything the deployment wizard could act on.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a helper inside the settings component because the
|
|
5
|
+
* component imports CSS modules, and a test that wants to CALL this decision cannot
|
|
6
|
+
* load them. Keeping the predicate dependency-free means the rule is exercised
|
|
7
|
+
* directly instead of being matched as text in the file that happens to use it --
|
|
8
|
+
* an assertion on source text cannot notice an inverted branch.
|
|
9
|
+
*
|
|
10
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Decide whether to render the deployment wizard.
|
|
14
|
+
*
|
|
15
|
+
* Every step in the plan executes ON A REMOTE HOST: the Host's `deploy.run` route
|
|
16
|
+
* refuses a request without a `hostId` ("Steps mutate a remote host"), and the MinerU
|
|
17
|
+
* steps are shell run there too -- they read the remote `/etc/os-release`, remote
|
|
18
|
+
* `uv`, remote `docker`. So the wizard is not an NFS-only block that a local
|
|
19
|
+
* deployment skips in part; with no SSH host configured it has nothing that can run,
|
|
20
|
+
* and seven disabled buttons are seven dead controls.
|
|
21
|
+
*
|
|
22
|
+
* A configured host is what makes the block meaningful, so that is the primary test.
|
|
23
|
+
* The deployment's own declared mode is the fallback: a `remote` deployment is one
|
|
24
|
+
* that intends to sync, so it keeps the wizard even before a host row exists -- the
|
|
25
|
+
* host list may be empty for a reason the reader can fix, and hiding the wizard would
|
|
26
|
+
* remove the only place that says so.
|
|
27
|
+
*
|
|
28
|
+
* A missing or unrecognised mode counts as `local`: the conservative direction,
|
|
29
|
+
* because local is the shape where the buttons would be a lie.
|
|
30
|
+
* @param hosts - the configured SSH hosts, as `deploy.hosts` reports them.
|
|
31
|
+
* @param storageMode - the deployment's declared mode, as `status` reports it.
|
|
32
|
+
* @returns true when the wizard has a remote target to act on.
|
|
33
|
+
*/
|
|
34
|
+
export declare function hasDeployTarget(hosts: readonly unknown[], storageMode: 'local' | 'remote' | undefined): boolean;
|