@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,174 @@
|
|
|
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
|
+
* Upload external files dropped onto a directory row.
|
|
78
|
+
*
|
|
79
|
+
* Distinct from the row-to-row move handled above: a file dragged in from the
|
|
80
|
+
* desktop carries no `KB_DRAG_MIME`, so it is the only case this callback serves.
|
|
81
|
+
* Omitted by a read-only caller, which keeps the rows from accepting a drop that
|
|
82
|
+
* would then be refused on the Host.
|
|
83
|
+
* @param relPath - the directory the files were dropped onto.
|
|
84
|
+
* @param files - the dropped files with paths relative to that directory.
|
|
85
|
+
*/
|
|
86
|
+
onUpload?: (relPath: string, files: readonly {
|
|
87
|
+
file: unknown;
|
|
88
|
+
relativePath: string;
|
|
89
|
+
}[]) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Called after a menu-driven delete or paste succeeded.
|
|
92
|
+
*
|
|
93
|
+
* Both change paths this component is currently holding -- a delete removes one, a
|
|
94
|
+
* paste adds one and empties another -- so the page has to reload rather than the
|
|
95
|
+
* component guessing which levels moved.
|
|
96
|
+
*/
|
|
97
|
+
onTreeChanged?: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Called after a rename succeeded, so the page reloads the tree.
|
|
100
|
+
*
|
|
101
|
+
* The listing cannot reload itself: it reads through `refreshTick`, which the page
|
|
102
|
+
* owns, and a rename changes the paths this component is currently holding.
|
|
103
|
+
*/
|
|
104
|
+
onRenamed?: () => void;
|
|
105
|
+
/**
|
|
106
|
+
* Render one row's action as "unpin" rather than "pin".
|
|
107
|
+
*
|
|
108
|
+
* The pinned band lists paths that are already pinned, so every row there offers
|
|
109
|
+
* the opposite action; deriving it from a flag keeps one row shape for both bands.
|
|
110
|
+
*/
|
|
111
|
+
unpinOnly?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Narrow the tree to rows whose name contains this text.
|
|
114
|
+
*
|
|
115
|
+
* Filtering happens over the rows already read, so it costs no request: the tree
|
|
116
|
+
* section header's search is the in-place narrowing, while the rail's field is the
|
|
117
|
+
* cross-directory search. A filter that matched nothing read as an empty tree, so
|
|
118
|
+
* the two empty states are kept apart.
|
|
119
|
+
*/
|
|
120
|
+
filter?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Start with every directory expanded.
|
|
123
|
+
*
|
|
124
|
+
* Used by the pinned band, where a pinned folder is only useful if its contents
|
|
125
|
+
* are already visible.
|
|
126
|
+
*/
|
|
127
|
+
expandAll?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* The action offered when the tree genuinely holds no documents.
|
|
130
|
+
*
|
|
131
|
+
* Distinct from a filter that matched nothing: an empty tree wants a way to make
|
|
132
|
+
* a document, while an unmatched filter wants a different search term.
|
|
133
|
+
*/
|
|
134
|
+
emptyAction?: ReactNode;
|
|
135
|
+
/**
|
|
136
|
+
* Move one entry into a directory, or pin it when the target is the pin band.
|
|
137
|
+
*
|
|
138
|
+
* Omitted by a caller that must stay read-only, which is how the search results
|
|
139
|
+
* list keeps its rows free of a gesture the Host would refuse there.
|
|
140
|
+
*/
|
|
141
|
+
onMove?: (from: string, toDirectory: string) => void;
|
|
142
|
+
/**
|
|
143
|
+
* The layout to render. `list` is the tree; `grid` is the document cards.
|
|
144
|
+
*
|
|
145
|
+
* Defaults to the tree so a caller that does not persist a preference behaves
|
|
146
|
+
* exactly as it did before the card view existed.
|
|
147
|
+
*/
|
|
148
|
+
viewMode?: 'list' | 'grid';
|
|
149
|
+
/**
|
|
150
|
+
* Enter multi-select. A callback rather than internal state because the batch
|
|
151
|
+
* bar's actions are the host's (they move and pin through the same routes a
|
|
152
|
+
* single drag uses), so the selection has to be visible to it.
|
|
153
|
+
*/
|
|
154
|
+
onSelectionChange?: (paths: readonly string[]) => void;
|
|
155
|
+
/** The current selection, so a batch action can act on it. */
|
|
156
|
+
selection?: readonly string[];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The drag payload's MIME type.
|
|
160
|
+
*
|
|
161
|
+
* Exported because the drop target that pins is NOT in this component: the pin
|
|
162
|
+
* band lives in the panel, and the two have to agree on the name of the thing
|
|
163
|
+
* being carried. The native drag payload is used rather than lifted React state
|
|
164
|
+
* for exactly that reason -- it crosses the component boundary the gesture
|
|
165
|
+
* crosses, without the panel having to hold a second copy of the tree's state.
|
|
166
|
+
*/
|
|
167
|
+
export declare const KB_DRAG_MIME = "application/x-dsh-kb-path";
|
|
168
|
+
/**
|
|
169
|
+
* Render the listing for the mount's top level, and one level per expanded
|
|
170
|
+
* directory.
|
|
171
|
+
* @param props - copy resolver and the open callback.
|
|
172
|
+
* @returns The listing panel.
|
|
173
|
+
*/
|
|
174
|
+
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;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glyph the left sidebar renders for the knowledge-base entry.
|
|
3
|
+
*
|
|
4
|
+
* `sidebar.panellist` is a list of icon buttons, so this component receives only a
|
|
5
|
+
* size and never renders a surface. The page the entry selects is a separate
|
|
6
|
+
* component registered against the `main` key.
|
|
7
|
+
*
|
|
8
|
+
* The glyph is the product's own icon component rather than a local path: the rail
|
|
9
|
+
* sits directly beside the other panel icons, and a hand-drawn stroke read visibly
|
|
10
|
+
* heavier than its neighbours. Using the product icon keeps the rail uniform and
|
|
11
|
+
* lets the size the sidebar passes through unchanged.
|
|
12
|
+
*
|
|
13
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
14
|
+
*/
|
|
15
|
+
/** Props the sidebar list passes to one entry. */
|
|
16
|
+
export interface KnowledgeMenuIconProps {
|
|
17
|
+
/** Glyph edge length in pixels, chosen by the sidebar for its current width. */
|
|
18
|
+
size?: number;
|
|
19
|
+
/** Whether this entry is the selected panel. */
|
|
20
|
+
active?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Render the knowledge-base glyph.
|
|
24
|
+
* @param props - sidebar-supplied glyph size.
|
|
25
|
+
* @returns The product icon sized for the sidebar rail.
|
|
26
|
+
*/
|
|
27
|
+
export declare function KnowledgeMenuIcon(props: KnowledgeMenuIconProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The knowledge-base management page.
|
|
3
|
+
*
|
|
4
|
+
* Selected by the left-sidebar entry and rendered into the `main` slot, so it
|
|
5
|
+
* replaces the conversation area. The page carries its own master-detail layout:
|
|
6
|
+
* the rail holds a menu above the knowledge tree, and the content region shows
|
|
7
|
+
* whichever surface the rail selected (a file preview, permissions, or indexing).
|
|
8
|
+
*
|
|
9
|
+
* The tree is this package's own listing rather than the file component from
|
|
10
|
+
* `dsh-better-sidebar`. That component renders icon and name only and builds its
|
|
11
|
+
* context menu internally, so it has nowhere to show whether a row is readable and
|
|
12
|
+
* nowhere to add a grant entry, while the actions its menu does offer (rename,
|
|
13
|
+
* delete, upload) edit a shared curated store rather than deciding who may see a
|
|
14
|
+
* document. The one thing worth keeping from it is the `@`-reference action, reused
|
|
15
|
+
* through its own helper so both trees insert the same reference.
|
|
16
|
+
*
|
|
17
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The session binding the page scopes its file tree with.
|
|
21
|
+
*
|
|
22
|
+
* It is a subscribable observable rather than a plain value because the session it
|
|
23
|
+
* names appears only after a Workspace is chosen, which regularly happens after
|
|
24
|
+
* this page has already mounted.
|
|
25
|
+
*/
|
|
26
|
+
export interface SessionSource {
|
|
27
|
+
/** Current binding; `key` is the session id, absent before a session exists. */
|
|
28
|
+
getSnapshot: () => {
|
|
29
|
+
key?: unknown;
|
|
30
|
+
};
|
|
31
|
+
/** Subscribe to changes; returns the unsubscribe function. */
|
|
32
|
+
subscribe: (listener: () => void) => () => void;
|
|
33
|
+
}
|
|
34
|
+
/** Values the `main` slot injects into the page. */
|
|
35
|
+
export interface KnowledgePanelInjected {
|
|
36
|
+
/** Locale-bound copy for this package's namespace. */
|
|
37
|
+
t: (key: string) => string;
|
|
38
|
+
/** Locale tag the slot bound for this namespace, so dates match the reader. */
|
|
39
|
+
locale?: string;
|
|
40
|
+
/** Source of the session the file tree resolves its scope from. */
|
|
41
|
+
sessionSource: SessionSource;
|
|
42
|
+
/**
|
|
43
|
+
* The client context, needed by the borrowed tree's `@` helper.
|
|
44
|
+
*
|
|
45
|
+
* That helper resolves the workspace root and the conversation input service
|
|
46
|
+
* itself, so it needs the context rather than the values it reads from it.
|
|
47
|
+
*/
|
|
48
|
+
ctx: unknown;
|
|
49
|
+
}
|
|
50
|
+
/** Props the page receives. */
|
|
51
|
+
export interface KnowledgePanelProps extends Partial<KnowledgePanelInjected> {
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Render the knowledge-base management page.
|
|
55
|
+
* @param props - injected locale copy and the current session id.
|
|
56
|
+
* @returns The master-detail page.
|
|
57
|
+
*/
|
|
58
|
+
export declare function KnowledgePanel(props: KnowledgePanelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The permissions surface: one grant table, read two ways.
|
|
3
|
+
*
|
|
4
|
+
* A grant says "this principal may do this action to this path". That is the right
|
|
5
|
+
* shape to STORE and the wrong shape to AUDIT: an administrator asking "what does
|
|
6
|
+
* 张三 have" and "who can see 制度.md" is asking two questions that one
|
|
7
|
+
* row-per-grant layout cannot answer without being read line by line.
|
|
8
|
+
*
|
|
9
|
+
* So the page has one body and two readings of it. The tabs switch which question
|
|
10
|
+
* is being asked; both render the same loaded grants, so the two can never
|
|
11
|
+
* disagree. No second endpoint exists because there is no second truth.
|
|
12
|
+
*
|
|
13
|
+
* The tables are the page's own body rather than a summary above one. An earlier
|
|
14
|
+
* version stacked both readings as bulleted lists and *also* drew a third,
|
|
15
|
+
* row-per-principal table underneath: three renderings of one payload, so the
|
|
16
|
+
* reader had to work out which of the three was authoritative before reading any
|
|
17
|
+
* of them. Two tabs over one table removes that question.
|
|
18
|
+
*
|
|
19
|
+
* @module @sparkelf/dsh-knowledge-base/client/KnowledgePermissionOverview
|
|
20
|
+
*/
|
|
21
|
+
import type { GrantView, KnowledgeGrantSubjectType } from '../api.ts';
|
|
22
|
+
import { type RoleSummary } from './roleSummary.ts';
|
|
23
|
+
/** Which reading of the grant table the tabs are showing. */
|
|
24
|
+
export type OverviewTab = 'person' | 'role' | 'resource';
|
|
25
|
+
/** Copy for the overview, supplied by the caller's locale record. */
|
|
26
|
+
export interface OverviewCopy {
|
|
27
|
+
tabByPerson: string;
|
|
28
|
+
tabByRole: string;
|
|
29
|
+
tabByResource: string;
|
|
30
|
+
empty: string;
|
|
31
|
+
/** Empty state per view, so "nobody is directly granted" is not shown for a role. */
|
|
32
|
+
emptyByPerson: string;
|
|
33
|
+
emptyByRole: string;
|
|
34
|
+
emptyByResource: string;
|
|
35
|
+
/** Where a principal's access comes from. */
|
|
36
|
+
sourceDirect: string;
|
|
37
|
+
/** Format "inherited via role X": `{role}` is replaced by the role code. */
|
|
38
|
+
sourceViaRole: (role: string) => string;
|
|
39
|
+
/** Shown when the user directory could not be read, so inheritance is unknowable. */
|
|
40
|
+
sourceUnavailable: string;
|
|
41
|
+
/** Word for a permission set that could not be fully determined. */
|
|
42
|
+
actionsUnknown: string;
|
|
43
|
+
actionsUnknownHint: string;
|
|
44
|
+
/** Column headers unique to the three views. */
|
|
45
|
+
colAccount: string;
|
|
46
|
+
colSource: string;
|
|
47
|
+
/** "{count} member(s)". */
|
|
48
|
+
roleMemberCount: (count: number) => string;
|
|
49
|
+
/** Pager copy, shared with the audit table's wording. */
|
|
50
|
+
pagerPrev: string;
|
|
51
|
+
pagerNext: string;
|
|
52
|
+
pagerStatus: (page: number, pageCount: number, total: number) => string;
|
|
53
|
+
pageSizeLabel: string;
|
|
54
|
+
pageSizeOption: (size: number) => string;
|
|
55
|
+
pathsHeld: (count: number) => string;
|
|
56
|
+
rolesHeld: string;
|
|
57
|
+
actionsHeld: string;
|
|
58
|
+
builtInRoles: string;
|
|
59
|
+
noActions: string;
|
|
60
|
+
/** Accessible name for the tab strip, for readers who arrive at it by keyboard. */
|
|
61
|
+
tabsLabel: string;
|
|
62
|
+
/** Stat-card labels: the four numbers the page leads with. */
|
|
63
|
+
statPrincipals: string;
|
|
64
|
+
statResources: string;
|
|
65
|
+
statGrants: string;
|
|
66
|
+
statRoles: string;
|
|
67
|
+
/** Shared column headers, person reading. */
|
|
68
|
+
colPrincipal: string;
|
|
69
|
+
colKind: string;
|
|
70
|
+
colPermission: string;
|
|
71
|
+
colResources: string;
|
|
72
|
+
colExpiry: string;
|
|
73
|
+
colActions: string;
|
|
74
|
+
/** Shared column headers, resource reading. */
|
|
75
|
+
colResource: string;
|
|
76
|
+
colHolders: string;
|
|
77
|
+
colKindOfResource: string;
|
|
78
|
+
revoke: string;
|
|
79
|
+
revokeBusy: string;
|
|
80
|
+
expiryNever: string;
|
|
81
|
+
kindFile: string;
|
|
82
|
+
kindDirectory: string;
|
|
83
|
+
/** Formats "N grants · M paths" under a person's name. */
|
|
84
|
+
grantsUnit: (count: number) => string;
|
|
85
|
+
resourcesUnit: (count: number) => string;
|
|
86
|
+
/**
|
|
87
|
+
* Resolves a principal kind and an action to its localized word.
|
|
88
|
+
*
|
|
89
|
+
* Supplied as callbacks rather than as a keyed map so this component never
|
|
90
|
+
* reaches into the locale record itself: a caller with its own wording (a test,
|
|
91
|
+
* another surface) can pass its own, and the type checker still enforces that
|
|
92
|
+
* every kind and action the table can print has a word.
|
|
93
|
+
*/
|
|
94
|
+
kindLabel: (kind: KnowledgeGrantSubjectType) => string;
|
|
95
|
+
actionLabel: (action: string) => string;
|
|
96
|
+
}
|
|
97
|
+
/** Props the parent panel supplies. */
|
|
98
|
+
export interface KnowledgePermissionOverviewProps {
|
|
99
|
+
/** The grant table already loaded by the permissions page. */
|
|
100
|
+
grants: readonly GrantView[];
|
|
101
|
+
/** Display names by `principalType:principalId`. */
|
|
102
|
+
labels?: Readonly<Record<string, string>>;
|
|
103
|
+
/**
|
|
104
|
+
* The DataOps account directory, when the caller could read it.
|
|
105
|
+
*
|
|
106
|
+
* Absent means the same as an unavailable read: inheritance cannot be attributed, and
|
|
107
|
+
* the page says so instead of listing accounts that look like they hold nothing.
|
|
108
|
+
*/
|
|
109
|
+
directory?: {
|
|
110
|
+
kind: 'available';
|
|
111
|
+
users: ReadonlyArray<{
|
|
112
|
+
id: string;
|
|
113
|
+
label: string;
|
|
114
|
+
username?: string;
|
|
115
|
+
roleCodes?: string[];
|
|
116
|
+
}>;
|
|
117
|
+
} | {
|
|
118
|
+
kind: 'unavailable';
|
|
119
|
+
reason: string;
|
|
120
|
+
};
|
|
121
|
+
/** Built-in role summaries, so the person view can explain what a role means. */
|
|
122
|
+
roles?: readonly RoleSummary[];
|
|
123
|
+
/** Locale copy. */
|
|
124
|
+
copy: OverviewCopy;
|
|
125
|
+
/**
|
|
126
|
+
* Asks the parent to confirm revoking the grants one row covers.
|
|
127
|
+
*
|
|
128
|
+
* The confirmation lives in the parent because it is a dialog, and a dialog in
|
|
129
|
+
* the middle of a table's row renderer would make the table responsible for
|
|
130
|
+
* ownership of the overlay. This component says *what* to revoke; the parent
|
|
131
|
+
* decides whether to ask first and then performs it.
|
|
132
|
+
*/
|
|
133
|
+
onRevoke: (grants: GrantView[], label: string) => void;
|
|
134
|
+
/** Disables every revoke control while a write is in flight. */
|
|
135
|
+
busy?: boolean;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Render the permission overview as stat cards over a three-tab table.
|
|
139
|
+
*
|
|
140
|
+
* THREE views, not two, because the old pair answered different questions under one
|
|
141
|
+
* heading: "by person" listed ROLE grants, so the only rows were 管理员 and 超级管理员
|
|
142
|
+
* and a reader concluding "these are the people with access" was reading the page
|
|
143
|
+
* correctly and being told something false. A role is not a person.
|
|
144
|
+
*
|
|
145
|
+
* @param props - grants, labels, directory, role summaries, copy and the revoke hook.
|
|
146
|
+
*/
|
|
147
|
+
export declare function KnowledgePermissionOverview(props: KnowledgePermissionOverviewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The permissions surface of the management page.
|
|
3
|
+
*
|
|
4
|
+
* This view reads and writes grants through the DSH Host, which forwards them to
|
|
5
|
+
* the DataOps backend. It deliberately holds no policy of its own: the backend's
|
|
6
|
+
* authorization resolves the principal, checks the caller's permission keys, and
|
|
7
|
+
* enforces what a grant means. The page only presents the result, so a rule
|
|
8
|
+
* change lands in one place.
|
|
9
|
+
*
|
|
10
|
+
* The page leads with its table. A grant list is what an operator opens this
|
|
11
|
+
* surface to read, so the data is the body of the page and the two things that
|
|
12
|
+
* CHANGE it -- creating a grant, revoking one -- are raised into dialogs. A form
|
|
13
|
+
* parked above the table would push the answer below the fold to make room for
|
|
14
|
+
* the question.
|
|
15
|
+
*
|
|
16
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
17
|
+
*/
|
|
18
|
+
import { type ReactNode } from 'react';
|
|
19
|
+
/** Props for the permissions surface. */
|
|
20
|
+
export interface KnowledgePermissionsProps {
|
|
21
|
+
/**
|
|
22
|
+
* Publishes this page's primary control to the panel header.
|
|
23
|
+
*
|
|
24
|
+
* The header is the only band at the top of the page, so a page-level action has
|
|
25
|
+
* to be handed up rather than drawn in a second title row.
|
|
26
|
+
*/
|
|
27
|
+
onAction?: (action: ReactNode) => void;
|
|
28
|
+
/** Locale-bound copy resolver. */
|
|
29
|
+
t: (key: string) => string;
|
|
30
|
+
/** Mount point the grants are scoped to. */
|
|
31
|
+
mountPoint: string;
|
|
32
|
+
/** Path the user selected, when the surface opened from a file row. */
|
|
33
|
+
initialPath?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Changes when a row's context menu picks a new path.
|
|
36
|
+
*
|
|
37
|
+
* The form's path field is seeded from `initialPath` once; without a signal that
|
|
38
|
+
* the seed changed, a second right-click would leave the field on the first path.
|
|
39
|
+
*/
|
|
40
|
+
reloadToken?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Render the permission editor.
|
|
44
|
+
* @param props - copy resolver and the mount point grants are scoped to.
|
|
45
|
+
* @returns The permission surface.
|
|
46
|
+
*/
|
|
47
|
+
export declare function KnowledgePermissions(props: KnowledgePermissionsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The progress strip at the bottom of the knowledge base panel.
|
|
3
|
+
*
|
|
4
|
+
* Uploading a document and extracting its text are two waits a reader cannot see
|
|
5
|
+
* otherwise: the drop appears to do nothing until the bytes land, and a scanned PDF
|
|
6
|
+
* looks uploaded while its `.md` sibling is still being produced. Both are reported
|
|
7
|
+
* here, in one place, at the bottom of the surface the reader is already looking at.
|
|
8
|
+
*
|
|
9
|
+
* It is a resident strip rather than a toast on purpose. A toast is gone before a slow
|
|
10
|
+
* parse finishes, and the reader's next question ("did it work?") has nowhere to go;
|
|
11
|
+
* this stays until the work is done and then folds to one line so it stops taking
|
|
12
|
+
* space without taking the record away.
|
|
13
|
+
*
|
|
14
|
+
* Nothing here is persisted. A refresh loses the history, which is the right trade: the
|
|
15
|
+
* files themselves are on disk, and a stale "uploading 40%" from yesterday would be a
|
|
16
|
+
* lie the reader cannot clear.
|
|
17
|
+
*
|
|
18
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
19
|
+
*/
|
|
20
|
+
/** One file's upload, as the strip shows it. */
|
|
21
|
+
export interface UploadTaskView {
|
|
22
|
+
/** The file's path relative to the drop target: the name the reader dropped. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** The tree-relative directory it is landing in, `''` for the mount root. */
|
|
25
|
+
dir: string;
|
|
26
|
+
/** Where this file is in its own lifecycle. */
|
|
27
|
+
state: 'waiting' | 'uploading' | 'done' | 'failed';
|
|
28
|
+
/** Why it failed, in the Host's own words. */
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Props for the progress strip. */
|
|
32
|
+
export interface KnowledgeProgressPanelProps {
|
|
33
|
+
/** Locale-bound copy resolver. */
|
|
34
|
+
t: (key: string) => string;
|
|
35
|
+
/** The batch currently uploading, oldest first; empty when nothing is in flight. */
|
|
36
|
+
uploads: readonly UploadTaskView[];
|
|
37
|
+
/**
|
|
38
|
+
* Whether to read extraction progress at all.
|
|
39
|
+
*
|
|
40
|
+
* False on a surface that cannot extract (no mount, or a caller without write
|
|
41
|
+
* access): polling a route whose answer can never be acted on is a request per
|
|
42
|
+
* reader that buys nothing.
|
|
43
|
+
*/
|
|
44
|
+
watchExtraction: boolean;
|
|
45
|
+
/** Called after a retry was accepted, so the caller can refresh its own views. */
|
|
46
|
+
onRetried?: (relPath: string) => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Render the bottom progress strip.
|
|
50
|
+
* @param props - the upload batch, whether to watch extraction, and the copy resolver.
|
|
51
|
+
* @returns The strip, or nothing when there is no work to show.
|
|
52
|
+
*/
|
|
53
|
+
export declare function KnowledgeProgressPanel(props: KnowledgeProgressPanelProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@`-mention picker: choose which conversation a knowledge file goes into.
|
|
3
|
+
*
|
|
4
|
+
* The explorer row's `@` button used to answer `if (sessionId === undefined) return`
|
|
5
|
+
* -- a page with no conversation open simply dropped the click, so the button looked
|
|
6
|
+
* broken. But "no conversation open" is exactly the state this page is in most of the
|
|
7
|
+
* time: it is a management surface, not a chat. The reference still has to land
|
|
8
|
+
* somewhere, so the click now asks where.
|
|
9
|
+
*
|
|
10
|
+
* Two things are deliberately borrowed rather than invented:
|
|
11
|
+
*
|
|
12
|
+
* The insertion itself is `referenceInChat`, the same contract the sidebar explorer
|
|
13
|
+
* and the viewer's selection popup call. That function owns the whole policy this
|
|
14
|
+
* dialog must not re-derive: which workspace root the mention is relative to, the
|
|
15
|
+
* chip spelling for files versus folders, the quoted form for paths with spaces, and
|
|
16
|
+
* the "degrade to a logged no-op" behaviour when a service is missing. Building a
|
|
17
|
+
* second insertion path here would be the one way to make the two surfaces disagree.
|
|
18
|
+
*
|
|
19
|
+
* The session list and the navigation are the host's own services, resolved lazily
|
|
20
|
+
* through `ctx.get` rather than injected: this dialog is mounted whether or not the
|
|
21
|
+
* user has ever opened a conversation, and a page that cannot see `uiSession` must
|
|
22
|
+
* still render (it would show an empty list, not crash).
|
|
23
|
+
*
|
|
24
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
25
|
+
*/
|
|
26
|
+
/** Props for the picker. */
|
|
27
|
+
export interface KnowledgeReferencePickerProps {
|
|
28
|
+
/** Whether the dialog is showing. */
|
|
29
|
+
open: boolean;
|
|
30
|
+
/** Close without referencing anything. */
|
|
31
|
+
onClose: () => void;
|
|
32
|
+
/** Absolute path of the entry the `@` button was pressed on. */
|
|
33
|
+
path: string;
|
|
34
|
+
/** Whether that entry is a directory (a folder mention rather than a file chip). */
|
|
35
|
+
isDir: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Every entry a multi-row selection asked to reference.
|
|
38
|
+
*
|
|
39
|
+
* Present only when the `@` button was pressed with several rows selected; the
|
|
40
|
+
* single-row case keeps using `path`/`isDir`. Each entry carries its own `isDir`
|
|
41
|
+
* because a selection may mix folders and documents, and the mention spelling
|
|
42
|
+
* differs between them.
|
|
43
|
+
*
|
|
44
|
+
* This is one dialog for both cases on purpose: "where should this go" is the same
|
|
45
|
+
* question whether one row or five are being referenced, and a second batch-only
|
|
46
|
+
* dialog would be a second place for the insertion contract to drift.
|
|
47
|
+
*/
|
|
48
|
+
targets?: readonly {
|
|
49
|
+
path: string;
|
|
50
|
+
isDir: boolean;
|
|
51
|
+
}[];
|
|
52
|
+
/** The client context; both the draft insert and the service lookups need it. */
|
|
53
|
+
ctx: unknown;
|
|
54
|
+
/** Locale-bound copy for this package's namespace. */
|
|
55
|
+
t: (key: string) => string;
|
|
56
|
+
/** Locale tag, so a session's timestamp reads in the reader's language. */
|
|
57
|
+
locale?: string;
|
|
58
|
+
/**
|
|
59
|
+
* The conversation the host currently has selected, read at call time.
|
|
60
|
+
*
|
|
61
|
+
* The New Session flow creates a conversation asynchronously and does not return
|
|
62
|
+
* its id, so the only way to name it afterwards is to ask the page's own session
|
|
63
|
+
* binding -- the same observable the file tree is scoped by. Reading it lazily
|
|
64
|
+
* rather than as a prop is what makes it current by the time the draft is ready.
|
|
65
|
+
*/
|
|
66
|
+
selectedSessionId?: () => string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Called once a reference was actually inserted, with the session that received it.
|
|
69
|
+
*
|
|
70
|
+
* The dialog closes itself either way; this exists so the page can report which
|
|
71
|
+
* conversation the file went to, which is the only feedback a reader gets when the
|
|
72
|
+
* target tab is not the one they were looking at.
|
|
73
|
+
*/
|
|
74
|
+
onReferenced?: (sessionId: string) => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The `@`-mention session picker.
|
|
78
|
+
* @param props - the entry to reference and the dialog's state.
|
|
79
|
+
* @returns The dialog, or null while closed.
|
|
80
|
+
*/
|
|
81
|
+
export declare function KnowledgeReferencePicker(props: KnowledgeReferencePickerProps): import("react").JSX.Element;
|