@epam/ai-dial-skill-editor 1.1.0-dev.184

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.
@@ -0,0 +1,5 @@
1
+ import { FC } from 'react';
2
+ import { SkillEditorProps } from '../../models/skill-editor-props';
3
+ /** Host-agnostic form for authoring a DIAL Skill's manifest and supporting files. */
4
+ export declare const SkillEditor: FC<SkillEditorProps>;
5
+ //# sourceMappingURL=SkillEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillEditor.d.ts","sourceRoot":"","sources":["../../../src/components/SkillEditor/SkillEditor.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAEL,EAAE,EASH,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,iCAAiC,CAAC;AAmBzC,qFAAqF;AACrF,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA2b5C,CAAC"}
package/index.css ADDED
@@ -0,0 +1,2 @@
1
+ ._title_l82ak_1{color:var(--se-title-color,var(--text-primary,#161b2d))}._helperText_l82ak_5{color:var(--se-helper-text-color,var(--text-secondary,#57647a))}._headerBorder_l82ak_9{border-bottom-color:var(--se-border-color,var(--stroke-tertiary,#e0e6f0))}._sidebarBorder_l82ak_13{border-inline-end-color:var(--se-border-color,var(--stroke-tertiary,#e0e6f0))}._actionBarBorder_l82ak_17{border-top-color:var(--se-border-color,var(--stroke-tertiary,#e0e6f0))}
2
+ /*$vite$:1*/
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { SkillEditor } from './components/SkillEditor/SkillEditor';
2
+ export type { SkillEditorColors, SkillEditorConflict, SkillEditorErrors, SkillEditorFileActions, SkillEditorLabels, SkillEditorProps, SkillEditorStyles, SkillEditorTypography, SkillEditorValues, SkillFileTreeNode, } from './models/skill-editor-props';
3
+ export { SkillFileNodeKind } from './types/skill-file-node-kind';
4
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC"}
package/index.js ADDED
@@ -0,0 +1,338 @@
1
+ import { buildCssVars as e, mergeClasses as t } from "@epam/ai-dial-chat-shared";
2
+ import { DialFileNodeType as n, DialFoldersTree as r } from "@epam/ai-dial-react-file-manager";
3
+ import { Accordion as i, CaptionText as ee, ConfirmationPopup as te, ConfirmationPopupVariant as ne, ErrorText as a, GhostButton as o, Input as re, LazyMarkdownEditor as s, NeutralButton as ie, PrimaryButton as c, Spinner as l, Textarea as ae } from "@epam/ai-dial-ui-kit";
4
+ import { IconPlus as oe, IconTrashX as se } from "@tabler/icons-react";
5
+ import { Suspense as ce, lazy as u, useCallback as d, useEffect as f, useId as p, useMemo as m, useRef as h, useState as g } from "react";
6
+ import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
7
+ //#region src/types/skill-editor-defaults.ts
8
+ var b = "SKILL.md", x = /* @__PURE__ */ function(e) {
9
+ return e.File = "file", e.Folder = "folder", e;
10
+ }({}), S = (e) => {
11
+ let t = e.lastIndexOf("/");
12
+ return t === -1 ? null : e.slice(0, t);
13
+ }, C = (e) => {
14
+ let t = e.lastIndexOf("/");
15
+ return t === -1 ? e : e.slice(t + 1);
16
+ }, le = (e) => {
17
+ let t = /* @__PURE__ */ new Map(), r = (e) => {
18
+ let r = t.get(e);
19
+ if (r) return r;
20
+ let i = {
21
+ path: e,
22
+ name: C(e),
23
+ nodeType: n.FOLDER,
24
+ parentPath: S(e),
25
+ folderId: e,
26
+ items: []
27
+ };
28
+ return t.set(e, i), i;
29
+ };
30
+ for (let i of e) {
31
+ if (i.kind === x.Folder) {
32
+ r(i.path);
33
+ continue;
34
+ }
35
+ t.set(i.path, {
36
+ path: i.path,
37
+ name: i.name,
38
+ nodeType: n.ITEM,
39
+ parentPath: S(i.path),
40
+ folderId: S(i.path) ?? ""
41
+ });
42
+ }
43
+ for (let t of [...e]) {
44
+ let e = S(t.path);
45
+ for (; e != null;) r(e), e = S(e);
46
+ }
47
+ let i = [];
48
+ for (let e of t.values()) {
49
+ if (e.parentPath == null) {
50
+ i.push(e);
51
+ continue;
52
+ }
53
+ let n = t.get(e.parentPath);
54
+ n ? n.items = [...n.items ?? [], e] : i.push(e);
55
+ }
56
+ return i;
57
+ }, w = {
58
+ title: "_title_l82ak_1",
59
+ helperText: "_helperText_l82ak_5",
60
+ headerBorder: "_headerBorder_l82ak_9",
61
+ sidebarBorder: "_sidebarBorder_l82ak_13",
62
+ actionBarBorder: "_actionBarBorder_l82ak_17"
63
+ }, ue = u(async () => {
64
+ let { MarkdownEditor: e } = await s();
65
+ return { default: e };
66
+ }), T = ({ initialValues: n, files: s, selectedPath: u, onSelectedPathChange: S, expandedPaths: C, onExpandedPathsChange: T, isLoading: de = !1, hasLoadError: fe = !1, isSubmitting: E = !1, errors: D, submitError: O, conflict: k, onReloadLatest: pe, isNameReadOnly: me = !1, onDirtyChange: A, fileActions: j, headerContent: he, onSubmit: ge, onCancel: _e, onRetry: ve, labels: ye, styles: M, dir: be, instructionsEditorTheme: xe = "light" }) => {
67
+ let [N, P] = g({
68
+ name: n?.name ?? "",
69
+ description: n?.description ?? "",
70
+ instructions: n?.instructions ?? ""
71
+ }), F = h(s);
72
+ f(() => {
73
+ P({
74
+ name: n?.name ?? "",
75
+ description: n?.description ?? "",
76
+ instructions: n?.instructions ?? ""
77
+ }), F.current = s;
78
+ }, [n]);
79
+ let I = (e) => JSON.stringify([...e].sort((e, t) => e.path.localeCompare(t.path))), L = h(!1);
80
+ f(() => {
81
+ let e = {
82
+ name: n?.name ?? "",
83
+ description: n?.description ?? "",
84
+ instructions: n?.instructions ?? ""
85
+ }, t = JSON.stringify(N) !== JSON.stringify(e) || I(s) !== I(F.current);
86
+ t !== L.current && (L.current = t, A?.(t));
87
+ }, [
88
+ N,
89
+ s,
90
+ n,
91
+ A
92
+ ]);
93
+ let [Se, Ce] = g(b), R = u ?? Se, z = d((e) => {
94
+ Ce(e), S?.(e);
95
+ }, [S]), [we, Te] = g([]), B = C ?? we, Ee = m(() => new Set(B), [B]), De = d((e) => {
96
+ let t = [...e];
97
+ Te(t), T?.(t);
98
+ }, [T]), [V, H] = g(null), [U, W] = g(), G = h(null), [Oe, ke] = g(!0), K = ye ?? {}, q = M?.colors, J = M?.typography ?? {}, Y = J.titleClassName ?? "dial-body-semi-text", Ae = J.helperTextClassName ?? "dial-tiny-semi-text", je = e({
99
+ "--se-title-color": q?.title,
100
+ "--se-helper-text-color": q?.helperText,
101
+ "--se-border-color": q?.border
102
+ }), Me = m(() => le([{
103
+ path: b,
104
+ name: b,
105
+ kind: x.File
106
+ }, ...s]), [s]), X = m(() => s.find((e) => e.path === R), [s, R]), Z = d((e) => {
107
+ z(e.path);
108
+ }, [z]), Ne = d((e) => e.path === "SKILL.md" ? [] : [{
109
+ key: "remove",
110
+ label: /* @__PURE__ */ v("span", {
111
+ className: "text-error",
112
+ children: K.removeLabel ?? "Remove"
113
+ }),
114
+ danger: !0,
115
+ icon: /* @__PURE__ */ v(se, {
116
+ size: 16,
117
+ className: "text-error",
118
+ "aria-hidden": !0
119
+ }),
120
+ onClick: () => H(e.path)
121
+ }], [K.removeLabel]), Pe = async (e) => {
122
+ let t = e.target.files?.[0];
123
+ if (e.target.value = "", !t) return;
124
+ let n = j.validatePath(t.name);
125
+ if (n) {
126
+ W(n);
127
+ return;
128
+ }
129
+ W(void 0);
130
+ try {
131
+ await j.onUploadFile(t, t.name);
132
+ } catch (e) {
133
+ W(e instanceof Error ? e.message : String(e));
134
+ }
135
+ }, Fe = p(), Ie = p(), Q = /* @__PURE__ */ y("div", {
136
+ className: "flex flex-col gap-2 desktop:gap-5",
137
+ children: [
138
+ /* @__PURE__ */ y("div", {
139
+ className: "flex items-center justify-between",
140
+ children: [/* @__PURE__ */ v("span", {
141
+ className: t(w.title, Y),
142
+ children: K.filesHeading ?? "Files"
143
+ }), /* @__PURE__ */ v(ie, {
144
+ label: K.addUploadLabel ?? "Upload from device",
145
+ iconBefore: /* @__PURE__ */ v(oe, {
146
+ size: 16,
147
+ "aria-hidden": !0
148
+ }),
149
+ onClick: () => G.current?.click()
150
+ })]
151
+ }),
152
+ /* @__PURE__ */ v("input", {
153
+ ref: G,
154
+ type: "file",
155
+ className: "hidden",
156
+ onChange: Pe
157
+ }),
158
+ U && /* @__PURE__ */ v(a, { text: U }),
159
+ /* @__PURE__ */ v("div", {
160
+ id: Fe,
161
+ role: "tree",
162
+ "aria-label": K.filesTreeAriaLabel ?? "Skill files",
163
+ children: /* @__PURE__ */ v(r, {
164
+ items: Me,
165
+ showFiles: !0,
166
+ selectedPath: R,
167
+ expandedPaths: Ee,
168
+ onExpandedPathsChange: De,
169
+ onItemClick: Z,
170
+ getContextMenuItems: Ne,
171
+ rootItemPath: ""
172
+ })
173
+ })
174
+ ]
175
+ }), Le = E ? K.savingStatusLabel ?? "Saving" : "", $ = /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(o, {
176
+ label: K.cancelLabel ?? "Cancel",
177
+ onClick: _e,
178
+ disabled: E
179
+ }), /* @__PURE__ */ v(c, {
180
+ label: K.createLabel ?? "Create",
181
+ iconBefore: E ? /* @__PURE__ */ v(l, {
182
+ size: 16,
183
+ ariaLabel: ""
184
+ }) : void 0,
185
+ onClick: () => ge(N),
186
+ disabled: E
187
+ })] });
188
+ return de ? /* @__PURE__ */ v("div", {
189
+ className: "flex h-full items-center justify-center",
190
+ children: /* @__PURE__ */ v(l, { ariaLabel: K.loadingAriaLabel ?? "Loading skill" })
191
+ }) : fe ? /* @__PURE__ */ y("div", {
192
+ role: "alert",
193
+ className: "flex flex-col items-center gap-4 p-8",
194
+ children: [/* @__PURE__ */ v(a, { text: K.loadErrorMessage ?? "Couldn't load this skill. Please try again." }), /* @__PURE__ */ v(c, {
195
+ label: K.retryLabel ?? "Retry",
196
+ onClick: ve
197
+ })]
198
+ }) : /* @__PURE__ */ y("div", {
199
+ dir: be,
200
+ className: "flex h-full flex-col",
201
+ style: je,
202
+ children: [
203
+ /* @__PURE__ */ v("span", {
204
+ role: "status",
205
+ "aria-live": "polite",
206
+ className: "sr-only",
207
+ id: Ie,
208
+ children: Le
209
+ }),
210
+ O && /* @__PURE__ */ v("div", {
211
+ role: "alert",
212
+ className: "px-4 pt-2",
213
+ children: /* @__PURE__ */ v(a, { text: O })
214
+ }),
215
+ k && /* @__PURE__ */ y("div", {
216
+ role: "alert",
217
+ className: "flex items-center gap-2 px-4 pt-2",
218
+ children: [/* @__PURE__ */ v(a, { text: k.message }), /* @__PURE__ */ v(o, {
219
+ label: K.reloadLatestLabel ?? "Reload latest",
220
+ onClick: pe
221
+ })]
222
+ }),
223
+ /* @__PURE__ */ y("div", {
224
+ className: t("hidden items-center justify-between gap-2 border-b px-4 py-2 desktop:flex desktop:px-8 desktop:pb-3 desktop:pt-3", w.headerBorder),
225
+ children: [/* @__PURE__ */ v("div", {
226
+ className: "flex min-w-0 flex-1 items-center gap-2",
227
+ children: he
228
+ }), /* @__PURE__ */ v("div", {
229
+ className: "flex shrink-0 items-center gap-2",
230
+ children: $
231
+ })]
232
+ }),
233
+ /* @__PURE__ */ y("div", {
234
+ className: "flex flex-1 flex-col gap-4 overflow-y-auto px-4 pb-20 desktop:flex-row desktop:gap-0 desktop:px-0 desktop:pb-0",
235
+ children: [
236
+ /* @__PURE__ */ v("div", {
237
+ className: "desktop:hidden",
238
+ children: /* @__PURE__ */ v(i, {
239
+ title: K.editingFileLabel ?? "Editing file",
240
+ description: X?.name ?? "SKILL.md",
241
+ expanded: Oe,
242
+ onToggle: ke,
243
+ ariaLabel: K.editingFileLabel ?? "Editing file",
244
+ children: Q
245
+ })
246
+ }),
247
+ /* @__PURE__ */ v("div", {
248
+ className: t("hidden border-e desktop:block desktop:w-[360px] desktop:shrink-0 desktop:px-8 desktop:py-6", w.sidebarBorder),
249
+ children: Q
250
+ }),
251
+ /* @__PURE__ */ y("div", {
252
+ className: "flex flex-1 flex-col gap-4 desktop:gap-5 desktop:px-8 desktop:py-6",
253
+ children: [/* @__PURE__ */ v("h2", {
254
+ className: t(w.title, Y),
255
+ children: R === "SKILL.md" ? b : K.selectedFileHeading?.(X?.name ?? R) ?? X?.name ?? R
256
+ }), R === "SKILL.md" ? /* @__PURE__ */ y(_, { children: [
257
+ /* @__PURE__ */ v(re, {
258
+ labelProps: {
259
+ label: K.nameLabel ?? "Name",
260
+ required: !0
261
+ },
262
+ value: N.name,
263
+ onChange: (e) => P((t) => ({
264
+ ...t,
265
+ name: e ?? ""
266
+ })),
267
+ placeholder: K.namePlaceholder ?? "good-morning-breakfast",
268
+ caption: D?.name ? void 0 : K.nameCaption ?? "Lowercase letters and hyphens only, no spaces. We'll reformat automatically if needed.",
269
+ error: D?.name,
270
+ invalid: !!D?.name,
271
+ disabled: me
272
+ }),
273
+ /* @__PURE__ */ v(ae, {
274
+ labelProps: {
275
+ label: K.descriptionLabel ?? "Description",
276
+ required: !0
277
+ },
278
+ value: N.description,
279
+ placeholder: K.descriptionPlaceholder ?? "What this skill does and when to use it",
280
+ onChange: (e) => P((t) => ({
281
+ ...t,
282
+ description: e
283
+ })),
284
+ error: D?.description,
285
+ invalid: !!D?.description
286
+ }),
287
+ /* @__PURE__ */ y("div", {
288
+ className: "flex flex-1 flex-col gap-2",
289
+ children: [
290
+ /* @__PURE__ */ y("span", {
291
+ className: "flex items-center gap-0.5",
292
+ children: [/* @__PURE__ */ v("span", {
293
+ className: t(w.helperText, Ae),
294
+ children: K.instructionsLabel ?? "Instructions"
295
+ }), /* @__PURE__ */ v("span", {
296
+ className: "dial-tiny-text text-error",
297
+ children: "*"
298
+ })]
299
+ }),
300
+ /* @__PURE__ */ v(ce, {
301
+ fallback: /* @__PURE__ */ v(l, { ariaLabel: K.instructionsLoadingAriaLabel ?? "Loading" }),
302
+ children: /* @__PURE__ */ v(ue, {
303
+ value: N.instructions,
304
+ onChange: (e) => P((t) => ({
305
+ ...t,
306
+ instructions: e
307
+ })),
308
+ theme: xe,
309
+ placeholder: K.instructionsPlaceholder ?? "Write the skill instructions in Markdown"
310
+ })
311
+ }),
312
+ D?.instructions && /* @__PURE__ */ v(a, { text: D.instructions })
313
+ ]
314
+ })
315
+ ] }) : X?.kind !== x.Folder && /* @__PURE__ */ v(ee, { text: K.supportingFileNote ?? "This supporting file is included in the skill package as-is. Remove it from the Files panel to replace its content." })]
316
+ })
317
+ ]
318
+ }),
319
+ /* @__PURE__ */ v("div", {
320
+ className: t("fixed inset-x-0 bottom-0 flex items-center gap-2 border-t p-3 desktop:hidden", w.actionBarBorder),
321
+ children: $
322
+ }),
323
+ V && /* @__PURE__ */ v(te, {
324
+ header: K.removeConfirmTitle ?? "Remove file",
325
+ description: K.removeConfirmMessage?.(V) ?? `Remove "${V}"? This cannot be undone.`,
326
+ confirmLabel: K.removeConfirmLabel ?? "Remove",
327
+ cancelLabel: K.removeCancelLabel ?? "Cancel",
328
+ variant: ne.Danger,
329
+ onConfirm: () => {
330
+ j.onRemoveNode(V), R === V && z("SKILL.md"), H(null);
331
+ },
332
+ onCancel: () => H(null)
333
+ })
334
+ ]
335
+ });
336
+ };
337
+ //#endregion
338
+ export { T as SkillEditor, x as SkillFileNodeKind };
@@ -0,0 +1,221 @@
1
+ import { ReactNode } from 'react';
2
+ import { SkillFileNodeKind } from '../types/skill-file-node-kind';
3
+ /**
4
+ * One supporting file or folder in the skill's file tree. The mandatory root
5
+ * `SKILL.md` manifest node is synthesised internally by `SkillEditor` and is
6
+ * never part of this list.
7
+ */
8
+ export interface SkillFileTreeNode {
9
+ /** Relative path from the skill root, using `/` separators. */
10
+ path: string;
11
+ /** Display name — the final path segment. */
12
+ name: string;
13
+ /** Whether this node is a file or a folder. */
14
+ kind: SkillFileNodeKind;
15
+ }
16
+ /** The skill fields the editor form holds. */
17
+ export interface SkillEditorValues {
18
+ /** Skill name, as typed by the user (host normalizes it on submit). */
19
+ name: string;
20
+ /** Short description of what the skill does. */
21
+ description: string;
22
+ /** The skill's instructions body (Markdown). */
23
+ instructions: string;
24
+ }
25
+ /**
26
+ * Inline validation messages, keyed by field. Resolved by the host, which owns
27
+ * the DIAL naming/storage contract the values must satisfy.
28
+ */
29
+ export interface SkillEditorErrors {
30
+ /** Message shown under the Name field. */
31
+ name?: string;
32
+ /** Message shown under the Description field. */
33
+ description?: string;
34
+ /** Message shown under the Instructions editor. */
35
+ instructions?: string;
36
+ }
37
+ /**
38
+ * Host-owned operations on the in-memory supporting-file set. The library
39
+ * calls these when the user uploads a file from their device or removes an
40
+ * entry; it never mutates `files` itself.
41
+ */
42
+ export interface SkillEditorFileActions {
43
+ /**
44
+ * Validates an uploaded file's path before it is added to the tree. Return
45
+ * an error message to block the addition, or `undefined` to accept it.
46
+ */
47
+ validatePath: (path: string) => string | undefined;
48
+ /**
49
+ * Called when the user picks a local file to upload as a supporting file.
50
+ * Resolves once the host has read and stored its content; rejects to
51
+ * surface an inline upload error instead of adding the node.
52
+ */
53
+ onUploadFile: (file: File, path: string) => Promise<void>;
54
+ /** Called after the user confirms removing a non-protected node. */
55
+ onRemoveNode: (path: string) => void;
56
+ }
57
+ /** Text overrides for `SkillEditor`. Every field has an English default. */
58
+ export interface SkillEditorLabels {
59
+ /** Files pane heading. Defaults to `'Files'`. */
60
+ filesHeading?: string;
61
+ /** Accessible name of the file tree region. Defaults to `'Skill files'`. */
62
+ filesTreeAriaLabel?: string;
63
+ /**
64
+ * Label of the control that opens the device file picker to add a
65
+ * supporting file. Defaults to `'Upload from device'`.
66
+ */
67
+ addUploadLabel?: string;
68
+ /** Accessible label of a node's remove action. Defaults to `'Remove'`. */
69
+ removeLabel?: string;
70
+ /** Title of the remove-confirmation prompt. Defaults to `'Remove file'`. */
71
+ removeConfirmTitle?: string;
72
+ /** Builds the remove-confirmation message for a given path. Defaults to a sentence naming the path. */
73
+ removeConfirmMessage?: (path: string) => string;
74
+ /** Confirm button label in the remove-confirmation prompt. Defaults to `'Remove'`. */
75
+ removeConfirmLabel?: string;
76
+ /** Cancel button label in the remove-confirmation prompt. Defaults to `'Cancel'`. */
77
+ removeCancelLabel?: string;
78
+ /** Collapsed mobile summary label. Defaults to `'Editing file'`. */
79
+ editingFileLabel?: string;
80
+ /** Main-pane heading, given the selected node's name. Defaults to the name itself. */
81
+ selectedFileHeading?: (name: string) => string;
82
+ /** Name field label. Defaults to `'Name'`. */
83
+ nameLabel?: string;
84
+ /** Name field placeholder. Defaults to `'good-morning-breakfast'`. */
85
+ namePlaceholder?: string;
86
+ /** Name field helper text. Defaults to `"Lowercase letters and hyphens only, no spaces. We'll reformat automatically if needed."`. */
87
+ nameCaption?: string;
88
+ /** Description field label. Defaults to `'Description'`. */
89
+ descriptionLabel?: string;
90
+ /** Description field placeholder. Defaults to `'What this skill does and when to use it'`. */
91
+ descriptionPlaceholder?: string;
92
+ /** Instructions editor label. Defaults to `'Instructions'`. */
93
+ instructionsLabel?: string;
94
+ /** Instructions editor placeholder. Defaults to `'Write the skill instructions in Markdown'`. */
95
+ instructionsPlaceholder?: string;
96
+ /** Create button label. Defaults to `'Create'`. */
97
+ createLabel?: string;
98
+ /** Cancel button label. Defaults to `'Cancel'`. */
99
+ cancelLabel?: string;
100
+ /** Retry button label shown in the load-error state. Defaults to `'Retry'`. */
101
+ retryLabel?: string;
102
+ /** Message shown when the skill could not be loaded. Defaults to `"Couldn't load this skill. Please try again."`. */
103
+ loadErrorMessage?: string;
104
+ /** Status announced while a save is in flight. Defaults to `'Saving'`. */
105
+ savingStatusLabel?: string;
106
+ /** Accessible label of the top-level loading spinner shown while `isLoading` is `true`. Defaults to `'Loading skill'`. */
107
+ loadingAriaLabel?: string;
108
+ /** Accessible label of the spinner shown while the Instructions Markdown editor's chunk is loading. Defaults to `'Loading'`. */
109
+ instructionsLoadingAriaLabel?: string;
110
+ /** Note shown in the main pane when a supporting file (not `SKILL.md` or a folder) is selected. Defaults to a sentence explaining content isn't editable here. */
111
+ supportingFileNote?: string;
112
+ /** Label of the "Reload latest" button rendered next to the host-supplied `conflict.message` in the conflict state. Defaults to `'Reload latest'`. */
113
+ reloadLatestLabel?: string;
114
+ }
115
+ /**
116
+ * Describes a save-time conflict (e.g. a stale ETag) distinct from
117
+ * `submitError` (an unrecoverable submit failure). When present, `SkillEditor`
118
+ * renders `conflict.message` plus a "Reload latest" control that calls
119
+ * `onReloadLatest` — the control never clears any field itself.
120
+ */
121
+ export interface SkillEditorConflict {
122
+ /** Host-resolved, already-translated conflict message. */
123
+ message: string;
124
+ }
125
+ /** CSS custom-property color overrides for `SkillEditor`. */
126
+ export interface SkillEditorColors {
127
+ /** Color of the "Files" and selected-file section headings. Defaults to `--text-primary`. */
128
+ title?: string;
129
+ /** Color of the hand-rendered Instructions field label. Defaults to `--text-secondary`. */
130
+ helperText?: string;
131
+ /** Border color of the desktop header row, the Files sidebar divider, and the mobile sticky action bar. Defaults to `--stroke-tertiary`. */
132
+ border?: string;
133
+ }
134
+ /** Typography class overrides for `SkillEditor`. */
135
+ export interface SkillEditorTypography {
136
+ /** Typography class applied to the "Files" and selected-file section headings. Defaults to `'dial-body-semi-text'`. */
137
+ titleClassName?: string;
138
+ /** Typography class applied to the hand-rendered Instructions field label. Defaults to `'dial-tiny-semi-text'`. */
139
+ helperTextClassName?: string;
140
+ }
141
+ /** Grouped style overrides for `SkillEditor`. */
142
+ export interface SkillEditorStyles {
143
+ /** Color overrides, applied as CSS custom properties. */
144
+ colors?: SkillEditorColors;
145
+ /** Typography class overrides. */
146
+ typography?: SkillEditorTypography;
147
+ }
148
+ /** Props for `SkillEditor`. */
149
+ export interface SkillEditorProps {
150
+ /**
151
+ * Values to seed the fields with. Changing this object's identity re-seeds
152
+ * the form, so hosts that load asynchronously should memoise it and only
153
+ * produce a new object once the data has arrived.
154
+ */
155
+ initialValues?: Partial<SkillEditorValues>;
156
+ /** Supporting files/folders, excluding the always-present root `SKILL.md` node, which the component synthesises. */
157
+ files: SkillFileTreeNode[];
158
+ /** Currently selected node path. Defaults to `'SKILL.md'` when omitted. */
159
+ selectedPath?: string;
160
+ /** Called when the selected node changes. Omit to let the component manage selection internally. */
161
+ onSelectedPathChange?: (path: string) => void;
162
+ /** Currently expanded folder paths. Omit to let the component manage expansion internally. */
163
+ expandedPaths?: string[];
164
+ /** Called when the set of expanded folder paths changes. */
165
+ onExpandedPathsChange?: (paths: string[]) => void;
166
+ /** Whether the skill being edited is still loading. Defaults to `false`. */
167
+ isLoading?: boolean;
168
+ /** Whether loading the skill failed; renders an error state with a retry instead of the form. Defaults to `false`. */
169
+ hasLoadError?: boolean;
170
+ /** Whether a save is in flight; disables submission. Defaults to `false`. */
171
+ isSubmitting?: boolean;
172
+ /** Inline validation messages to render under the fields. */
173
+ errors?: SkillEditorErrors;
174
+ /** General submit-time error (e.g. a naming conflict or a server error) rendered in a `role="alert"` region. */
175
+ submitError?: string;
176
+ /**
177
+ * A save-time conflict (e.g. a stale ETag), distinct from `submitError`.
178
+ * When present, renders `conflict.message` plus a "Reload latest" control
179
+ * calling `onReloadLatest`. Omit when there is no conflict.
180
+ */
181
+ conflict?: SkillEditorConflict;
182
+ /** Called when the conflict state's "Reload latest" control is activated. Required when `conflict` can be set. */
183
+ onReloadLatest?: () => void;
184
+ /**
185
+ * When `true`, the Name field renders read-only — its value is still
186
+ * included in submitted values unchanged. The host sets this in edit mode,
187
+ * since DIAL Core has no rename/move operation for a skill; the library
188
+ * itself has no notion of "edit mode" and infers no policy from this flag
189
+ * beyond disabling the field.
190
+ */
191
+ isNameReadOnly?: boolean;
192
+ /**
193
+ * Called whenever any field value or the file-tree state diverges from its
194
+ * most recently seeded `initialValues`/`files` (`true`), and again once it
195
+ * returns to exactly that seeded state (`false`).
196
+ */
197
+ onDirtyChange?: (isDirty: boolean) => void;
198
+ /** File-tree mutation operations. */
199
+ fileActions: SkillEditorFileActions;
200
+ /**
201
+ * Host-rendered content (e.g. a back button and page title) placed at the
202
+ * start of the desktop header row, before the Cancel/Create actions. The
203
+ * library renders it verbatim with no knowledge of what it contains.
204
+ */
205
+ headerContent?: ReactNode;
206
+ /** Called with the current values when the form is submitted. */
207
+ onSubmit: (values: SkillEditorValues) => void;
208
+ /** Called when the form is dismissed without saving. */
209
+ onCancel: () => void;
210
+ /** Called when the retry button in the load-error state is activated. */
211
+ onRetry?: () => void;
212
+ /** Text overrides. */
213
+ labels?: SkillEditorLabels;
214
+ /** Style overrides. */
215
+ styles?: SkillEditorStyles;
216
+ /** Explicit direction override. Omit to inherit from the ambient `dir` attribute. */
217
+ dir?: 'ltr' | 'rtl';
218
+ /** Theme applied to the Instructions Markdown editor, resolved by the host from its own theme state. Defaults to `'light'`. */
219
+ instructionsEditorTheme?: 'light' | 'dark';
220
+ }
221
+ //# sourceMappingURL=skill-editor-props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-editor-props.d.ts","sourceRoot":"","sources":["../../src/models/skill-editor-props.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@epam/ai-dial-skill-editor",
3
+ "description": "Host-agnostic form for authoring a DIAL Skill, with an inline supporting-file tree",
4
+ "version": "1.1.0-dev.184",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./index.js",
8
+ "module": "./index.js",
9
+ "types": "./index.d.ts",
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ ".": {
13
+ "types": "./index.d.ts",
14
+ "import": "./index.js",
15
+ "default": "./index.js"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "@epam/ai-dial-chat-shared": "1.1.0-dev.184",
20
+ "@epam/ai-dial-ui-kit": "^0.13.0-dev.26",
21
+ "@epam/ai-dial-react-file-manager": "^0.1.0-dev.17",
22
+ "@tabler/icons-react": "^3.0.0",
23
+ "react": "^19.0.0"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/epam/ai-dial-chat.git",
28
+ "directory": "libs/skill-editor"
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ /** Relative path of the mandatory root manifest node, always present and always first in the tree. */
2
+ export declare const SKILL_MANIFEST_PATH = "SKILL.md";
3
+ //# sourceMappingURL=skill-editor-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-editor-defaults.d.ts","sourceRoot":"","sources":["../../src/types/skill-editor-defaults.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,eAAO,MAAM,mBAAmB,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Kind of entry in the skill's file tree. */
2
+ export declare enum SkillFileNodeKind {
3
+ /** A regular file, including the protected root `SKILL.md`. */
4
+ File = "file",
5
+ /** A grouping folder that may contain other files/folders. */
6
+ Folder = "folder"
7
+ }
8
+ //# sourceMappingURL=skill-file-node-kind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-file-node-kind.d.ts","sourceRoot":"","sources":["../../src/types/skill-file-node-kind.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,oBAAY,iBAAiB;IAC3B,+DAA+D;IAC/D,IAAI,SAAS;IACb,8DAA8D;IAC9D,MAAM,WAAW;CAClB"}
@@ -0,0 +1,10 @@
1
+ import { DialFile } from '@epam/ai-dial-react-file-manager';
2
+ import { SkillFileTreeNode } from '../models/skill-editor-props';
3
+ /**
4
+ * Builds the nested `DialFile[]` tree `DialFoldersTree` expects from the flat
5
+ * `SkillFileTreeNode[]` list, synthesising any implicit intermediate folders
6
+ * (e.g. `agents/analyzer.md` implies an `agents` folder even when no explicit
7
+ * folder node was added for it).
8
+ */
9
+ export declare const buildDialFileTree: (nodes: SkillFileTreeNode[]) => DialFile[];
10
+ //# sourceMappingURL=file-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-tree.d.ts","sourceRoot":"","sources":["../../src/utils/file-tree.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAatE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,iBAAiB,EAAE,KAAG,QAAQ,EAuDtE,CAAC"}