pith-ui 0.1.0 → 0.1.4
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/AI.md +20 -1
- package/API.md +834 -272
- package/README.md +3 -1
- package/dist/composer.d.ts.map +1 -1
- package/dist/composer.js +48 -29
- package/dist/composer.js.map +1 -1
- package/dist/content/conversation-reader.d.ts.map +1 -1
- package/dist/content/conversation-reader.js +8 -1
- package/dist/content/conversation-reader.js.map +1 -1
- package/dist/content/conversation-view.d.ts +21 -4
- package/dist/content/conversation-view.d.ts.map +1 -1
- package/dist/content/conversation-view.js +72 -13
- package/dist/content/conversation-view.js.map +1 -1
- package/dist/content/image-viewer.d.ts +4 -2
- package/dist/content/image-viewer.d.ts.map +1 -1
- package/dist/content/image-viewer.js +36 -15
- package/dist/content/image-viewer.js.map +1 -1
- package/dist/content/paged-preview.d.ts +1 -1
- package/dist/content/paged-preview.d.ts.map +1 -1
- package/dist/content/paged-preview.js +10 -5
- package/dist/content/paged-preview.js.map +1 -1
- package/dist/content/views.d.ts +3 -0
- package/dist/content/views.d.ts.map +1 -1
- package/dist/content/views.js +110 -41
- package/dist/content/views.js.map +1 -1
- package/dist/content/welcome.d.ts.map +1 -1
- package/dist/content/welcome.js +19 -7
- package/dist/content/welcome.js.map +1 -1
- package/dist/dom.js +1 -1
- package/dist/dom.js.map +1 -1
- package/dist/files/panels.d.ts +8 -1
- package/dist/files/panels.d.ts.map +1 -1
- package/dist/files/panels.js +235 -120
- package/dist/files/panels.js.map +1 -1
- package/dist/files/systems.d.ts.map +1 -1
- package/dist/files/systems.js +81 -91
- package/dist/files/systems.js.map +1 -1
- package/dist/forms/actions.d.ts +28 -7
- package/dist/forms/actions.d.ts.map +1 -1
- package/dist/forms/actions.js +59 -11
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/custom-connection.d.ts +2 -1
- package/dist/forms/custom-connection.d.ts.map +1 -1
- package/dist/forms/custom-connection.js +36 -28
- package/dist/forms/custom-connection.js.map +1 -1
- package/dist/forms/dialog-frame.d.ts +2 -0
- package/dist/forms/dialog-frame.d.ts.map +1 -1
- package/dist/forms/dialog-frame.js +46 -26
- package/dist/forms/dialog-frame.js.map +1 -1
- package/dist/forms/history.d.ts +1 -1
- package/dist/forms/history.d.ts.map +1 -1
- package/dist/forms/history.js +78 -15
- package/dist/forms/history.js.map +1 -1
- package/dist/forms/identity.d.ts +12 -0
- package/dist/forms/identity.d.ts.map +1 -1
- package/dist/forms/identity.js +16 -0
- package/dist/forms/identity.js.map +1 -1
- package/dist/forms/model-editor.d.ts +8 -3
- package/dist/forms/model-editor.d.ts.map +1 -1
- package/dist/forms/model-editor.js +73 -62
- package/dist/forms/model-editor.js.map +1 -1
- package/dist/forms/panels.d.ts +103 -7
- package/dist/forms/panels.d.ts.map +1 -1
- package/dist/forms/panels.js +183 -77
- package/dist/forms/panels.js.map +1 -1
- package/dist/forms/provider-login.d.ts.map +1 -1
- package/dist/forms/provider-login.js +25 -39
- package/dist/forms/provider-login.js.map +1 -1
- package/dist/forms/resource-editor.d.ts +9 -5
- package/dist/forms/resource-editor.d.ts.map +1 -1
- package/dist/forms/resource-editor.js +20 -9
- package/dist/forms/resource-editor.js.map +1 -1
- package/dist/foundations.d.ts +95 -0
- package/dist/foundations.d.ts.map +1 -0
- package/dist/foundations.js +299 -0
- package/dist/foundations.js.map +1 -0
- package/dist/navigation/context-menu.d.ts +8 -1
- package/dist/navigation/context-menu.d.ts.map +1 -1
- package/dist/navigation/context-menu.js +67 -50
- package/dist/navigation/context-menu.js.map +1 -1
- package/dist/navigation/navigation.d.ts +55 -7
- package/dist/navigation/navigation.d.ts.map +1 -1
- package/dist/navigation/navigation.js +167 -18
- package/dist/navigation/navigation.js.map +1 -1
- package/dist/operation.d.ts +12 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +49 -0
- package/dist/operation.js.map +1 -0
- package/dist/primitives.d.ts +11 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +164 -301
- package/dist/primitives.js.map +1 -1
- package/dist/reconcile.d.ts +11 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +36 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/select.d.ts +14 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +304 -0
- package/dist/select.js.map +1 -0
- package/dist/styles.css +452 -382
- package/dist/surface.d.ts +60 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +344 -0
- package/dist/surface.js.map +1 -0
- package/dist/tasks/approval.d.ts.map +1 -1
- package/dist/tasks/approval.js +86 -45
- package/dist/tasks/approval.js.map +1 -1
- package/dist/tasks/permission-element.d.ts +3 -0
- package/dist/tasks/permission-element.d.ts.map +1 -0
- package/dist/tasks/permission-element.js +10 -0
- package/dist/tasks/permission-element.js.map +1 -0
- package/dist/tasks/permission.d.ts +5 -3
- package/dist/tasks/permission.d.ts.map +1 -1
- package/dist/tasks/permission.js +47 -22
- package/dist/tasks/permission.js.map +1 -1
- package/dist/tasks/runtime.d.ts.map +1 -1
- package/dist/tasks/runtime.js +30 -13
- package/dist/tasks/runtime.js.map +1 -1
- package/dist/workspace.d.ts +58 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +202 -44
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
package/dist/files/panels.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { dialogHeading, escape, icon, uniqueId, setText } from '../dom.js';
|
|
2
2
|
import { createPagedPreview } from '../content/paged-preview.js';
|
|
3
|
+
import { adoptProductParts } from '../foundations.js';
|
|
4
|
+
import { createButton } from '../primitives.js';
|
|
5
|
+
import { bindContentClose, installAnchoredSurface } from '../surface.js';
|
|
6
|
+
import { reconcileKeyed } from '../reconcile.js';
|
|
3
7
|
export function fileActionsMarkup(options) {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
8
|
+
const scope = `data-idle-action ${options.kind === 'resource' ? 'data-idle-workspace' : 'data-idle-conversation'}="${escape(options.ownerId)}"`;
|
|
9
|
+
const attributes = `${scope} data-file-kind="${escape(options.kind)}" data-file-owner="${escape(options.ownerId)}" data-file-path="${escape(options.path)}"`;
|
|
10
|
+
const preview = options.preview ? `<button type="button" class="pui-secondary-button" ${scope} data-preview-path="${escape(options.path)}" data-preview-id="${escape(options.ownerId)}">Preview</button>` : '';
|
|
6
11
|
const copy = options.copyMode && options.copyMode !== 'none' ? `<button type="button" class="pui-secondary-button" ${attributes} data-file-action="${options.copyMode === 'file' ? 'copy' : 'copy-path'}">${options.copyMode === 'file' ? 'Copy file' : 'Copy path'}</button>` : '';
|
|
7
12
|
return `<div class="pui-file-actions">${preview}<button type="button" class="pui-secondary-button" ${attributes} data-file-action="open">Open</button><button type="button" class="pui-secondary-button" ${attributes} data-file-action="reveal">Reveal</button>${copy}</div>`;
|
|
8
13
|
}
|
|
@@ -10,14 +15,25 @@ export function createFileActions(options) {
|
|
|
10
15
|
const template = document.createElement('template');
|
|
11
16
|
template.innerHTML = fileActionsMarkup(options);
|
|
12
17
|
const element = template.content.firstElementChild;
|
|
18
|
+
const parts = adoptProductParts(element);
|
|
19
|
+
let destroyed = false;
|
|
13
20
|
const click = (event) => {
|
|
14
21
|
const target = event.target.closest('button');
|
|
15
|
-
if (target && !target.disabled)
|
|
22
|
+
if (target && element.contains(target) && !target.disabled) {
|
|
23
|
+
event.stopPropagation();
|
|
16
24
|
options.onAction?.(target.dataset.fileAction || 'preview');
|
|
25
|
+
}
|
|
17
26
|
};
|
|
18
27
|
if (options.onAction)
|
|
19
28
|
element.addEventListener('click', click);
|
|
20
|
-
return {
|
|
29
|
+
return {
|
|
30
|
+
element,
|
|
31
|
+
setDisabled(disabled) { if (!destroyed)
|
|
32
|
+
for (const button of element.querySelectorAll('button'))
|
|
33
|
+
parts.action(button).update({ disabled }); },
|
|
34
|
+
destroy() { if (destroyed)
|
|
35
|
+
return; destroyed = true; parts.destroy(); element.removeEventListener('click', click); element.remove(); },
|
|
36
|
+
};
|
|
21
37
|
}
|
|
22
38
|
export function createArtifactPanel(options) {
|
|
23
39
|
const { container: element, control, toggle, count } = options;
|
|
@@ -26,26 +42,37 @@ export function createArtifactPanel(options) {
|
|
|
26
42
|
const loading = element.querySelector('[role="status"]');
|
|
27
43
|
const rows = element.querySelector('[data-file-rows]');
|
|
28
44
|
const error = element.querySelector('[role="alert"]');
|
|
45
|
+
const parts = adoptProductParts(element);
|
|
29
46
|
const entries = new Map();
|
|
30
|
-
let open = false;
|
|
31
47
|
let destroyed = false;
|
|
48
|
+
const surface = installAnchoredSurface({
|
|
49
|
+
trigger: toggle,
|
|
50
|
+
panel: element,
|
|
51
|
+
position: false,
|
|
52
|
+
closeOnFocusOut: true,
|
|
53
|
+
exclusions: [control, ...(options.dismissExclusions ?? [])],
|
|
54
|
+
canOpen: () => !control.hidden,
|
|
55
|
+
canEscape: () => !document.querySelector('dialog[open]'),
|
|
56
|
+
onToggle(open) { if (open)
|
|
57
|
+
element.focus(); },
|
|
58
|
+
});
|
|
32
59
|
function setOpen(value, restoreFocus = false) {
|
|
33
60
|
if (destroyed)
|
|
34
61
|
return;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
element.focus();
|
|
40
|
-
else if (restoreFocus && !control.hidden)
|
|
41
|
-
toggle.focus();
|
|
62
|
+
if (value)
|
|
63
|
+
surface.open();
|
|
64
|
+
else
|
|
65
|
+
surface.close(restoreFocus && !control.hidden);
|
|
42
66
|
}
|
|
67
|
+
const abort = new AbortController();
|
|
68
|
+
toggle.addEventListener('click', () => setOpen(!surface.isOpen()), { signal: abort.signal });
|
|
69
|
+
element.addEventListener('click', event => { if (event.target.closest('[data-artifacts-close]'))
|
|
70
|
+
setOpen(false, true); }, { signal: abort.signal });
|
|
43
71
|
return {
|
|
44
|
-
element, setOpen, get open() { return
|
|
72
|
+
element, setOpen, get open() { return surface.isOpen(); },
|
|
45
73
|
update(state) {
|
|
46
74
|
if (destroyed)
|
|
47
75
|
return;
|
|
48
|
-
const focused = document.activeElement;
|
|
49
76
|
control.hidden = !state.ownerId || (!state.files.length && !state.error);
|
|
50
77
|
if (control.hidden)
|
|
51
78
|
setOpen(false);
|
|
@@ -57,149 +84,218 @@ export function createArtifactPanel(options) {
|
|
|
57
84
|
total.textContent = number;
|
|
58
85
|
loading.hidden = !state.loading;
|
|
59
86
|
error.hidden = !state.error;
|
|
60
|
-
error.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
entries.delete(key);
|
|
66
|
-
}
|
|
67
|
-
let cursor = rows.firstElementChild;
|
|
68
|
-
for (const file of state.files) {
|
|
69
|
-
const key = `${state.ownerId}/${file.path}`;
|
|
70
|
-
let entry = entries.get(key);
|
|
71
|
-
if (!entry) {
|
|
87
|
+
parts.feedback(error).update({ message: state.error, tone: 'error' });
|
|
88
|
+
reconcileKeyed(rows, entries, state.files, {
|
|
89
|
+
key: file => `${state.ownerId}/${file.path}`,
|
|
90
|
+
element: entry => entry.element,
|
|
91
|
+
create(file) {
|
|
72
92
|
const article = document.createElement('article');
|
|
73
93
|
article.className = 'pui-file-card';
|
|
74
94
|
article.innerHTML = `${icon('file')}<div class="pui-file-info"><strong></strong><span class="pui-file-path">${escape(file.path)}</span></div>${fileActionsMarkup({ kind: 'artifact', ownerId: state.ownerId, path: file.path, preview: true, copyMode: state.copyMode })}`;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused)
|
|
89
|
-
focused.focus({ preventScroll: true });
|
|
95
|
+
return { element: article, name: article.querySelector('strong'), copy: article.querySelector('[data-file-action="copy"], [data-file-action="copy-path"]') };
|
|
96
|
+
},
|
|
97
|
+
update(entry, file) {
|
|
98
|
+
setText(entry.name, file.name);
|
|
99
|
+
const copyAction = state.copyMode === 'file' ? 'copy' : 'copy-path';
|
|
100
|
+
if (entry.copy.dataset.fileAction !== copyAction)
|
|
101
|
+
entry.copy.dataset.fileAction = copyAction;
|
|
102
|
+
if (entry.copy.dataset.copyPending !== 'true')
|
|
103
|
+
setText(entry.copy, state.copyMode === 'file' ? 'Copy file' : 'Copy path');
|
|
104
|
+
},
|
|
105
|
+
destroy() { },
|
|
106
|
+
});
|
|
107
|
+
parts.update();
|
|
90
108
|
},
|
|
91
|
-
destroy() { destroyed = true; entries.clear(); element.replaceChildren(); },
|
|
109
|
+
destroy() { destroyed = true; surface.destroy(); abort.abort(); parts.destroy(); entries.clear(); element.replaceChildren(); },
|
|
92
110
|
};
|
|
93
111
|
}
|
|
94
112
|
export function createResourcesPanel(options = {}) {
|
|
95
113
|
const element = options.container || document.createElement('div');
|
|
96
114
|
const ids = { titleId: options.titleId || uniqueId('pui-resources-title'), dialogId: options.dialogId || uniqueId('pui-resources-dialog'), listId: options.listId || uniqueId('pui-resources-list'), errorId: options.errorId || uniqueId('pui-resources-error') };
|
|
97
|
-
const close = (
|
|
98
|
-
if (!event.target.closest('[data-close]'))
|
|
99
|
-
return;
|
|
100
|
-
if (options.onClose)
|
|
101
|
-
options.onClose();
|
|
102
|
-
else {
|
|
103
|
-
const dialog = element.closest('dialog');
|
|
104
|
-
if (dialog?.open)
|
|
105
|
-
dialog.close();
|
|
106
|
-
else
|
|
107
|
-
element.hidden = true;
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
element.addEventListener('click', close);
|
|
115
|
+
const close = bindContentClose(element, { dialogId: ids.dialogId, onClose: options.onClose });
|
|
111
116
|
let list, error;
|
|
112
117
|
let destroyed = false;
|
|
113
|
-
|
|
118
|
+
const parts = adoptProductParts(element);
|
|
119
|
+
const buttons = [];
|
|
120
|
+
const rowButtons = [];
|
|
121
|
+
const sections = new Map();
|
|
122
|
+
let diagnostics;
|
|
123
|
+
let instructionButton;
|
|
124
|
+
const createActions = new Map();
|
|
125
|
+
let createDisabled = false;
|
|
126
|
+
const button = (label, onAction, row = false, create = false) => { const block = createButton({ label, appearance: 'secondary', onAction, disabled: create && createDisabled }); (row ? rowButtons : buttons).push(block); if (create)
|
|
127
|
+
createActions.set(block, label); return block.element; };
|
|
128
|
+
const clearButtons = (list = buttons) => { for (const block of list.splice(0)) {
|
|
129
|
+
createActions.delete(block);
|
|
130
|
+
block.destroy();
|
|
131
|
+
} };
|
|
132
|
+
function updateSlot(parent, slot, source) {
|
|
133
|
+
if (slot.source === source)
|
|
134
|
+
return;
|
|
135
|
+
for (const node of slot.nodes)
|
|
136
|
+
if (node.parentNode === parent)
|
|
137
|
+
node.remove();
|
|
138
|
+
slot.source = source;
|
|
139
|
+
slot.nodes = source ? source instanceof DocumentFragment ? [...source.childNodes] : [source] : [];
|
|
140
|
+
}
|
|
141
|
+
function row(type) {
|
|
114
142
|
const outer = document.createElement('article');
|
|
115
143
|
outer.className = type === 'instructions' ? 'pui-resource-card' : 'pui-file-card';
|
|
116
144
|
const body = type === 'instructions' ? document.createElement('div') : outer;
|
|
117
145
|
if (type === 'instructions')
|
|
118
146
|
body.className = 'pui-file-card';
|
|
119
|
-
body.innerHTML = `${type === 'template' ? '' : icon('file')}<div class="pui-file-info"><strong
|
|
120
|
-
|
|
121
|
-
body.append(entry.fileActions);
|
|
122
|
-
if (entry.actions)
|
|
123
|
-
body.append(entry.actions);
|
|
147
|
+
body.innerHTML = `${type === 'template' ? '' : icon('file')}<div class="pui-file-info"><strong></strong>${type !== 'instructions' ? '<p></p>' : ''}<span class="pui-file-path"></span></div>`;
|
|
148
|
+
let content;
|
|
124
149
|
if (type === 'instructions') {
|
|
125
150
|
outer.append(body);
|
|
126
151
|
const details = document.createElement('details');
|
|
127
152
|
details.className = 'pui-resource-preview';
|
|
128
|
-
details.innerHTML =
|
|
153
|
+
details.innerHTML = '<summary>View instructions</summary><pre></pre>';
|
|
129
154
|
outer.append(details);
|
|
155
|
+
content = details.querySelector('pre');
|
|
156
|
+
}
|
|
157
|
+
return { element: outer, body, name: body.querySelector('strong'), description: body.querySelector('p') || undefined, path: body.querySelector('.pui-file-path'), content, slots: [{ nodes: [] }, { nodes: [] }] };
|
|
158
|
+
}
|
|
159
|
+
function updateRow(row, entry) {
|
|
160
|
+
setText(row.name, entry.name);
|
|
161
|
+
setText(row.path, entry.path);
|
|
162
|
+
if (row.description)
|
|
163
|
+
setText(row.description, entry.description || '');
|
|
164
|
+
if (row.content)
|
|
165
|
+
setText(row.content, entry.content || '');
|
|
166
|
+
updateSlot(row.body, row.slots[0], entry.fileActions);
|
|
167
|
+
updateSlot(row.body, row.slots[1], entry.actions);
|
|
168
|
+
let cursor = row.body.querySelector('.pui-file-info').nextSibling;
|
|
169
|
+
for (const node of row.slots.flatMap(slot => slot.nodes)) {
|
|
170
|
+
if (node !== cursor)
|
|
171
|
+
row.body.insertBefore(node, cursor);
|
|
172
|
+
cursor = node.nextSibling;
|
|
130
173
|
}
|
|
131
|
-
|
|
174
|
+
}
|
|
175
|
+
function section() {
|
|
176
|
+
const element = document.createElement('section');
|
|
177
|
+
element.className = 'pui-resource-section';
|
|
178
|
+
return { element, rows: new Map(), create: { nodes: [] } };
|
|
132
179
|
}
|
|
133
180
|
return {
|
|
134
181
|
element,
|
|
135
182
|
open(workspaceName, toolbar) {
|
|
136
183
|
if (destroyed)
|
|
137
184
|
return;
|
|
185
|
+
clearButtons();
|
|
186
|
+
clearButtons(rowButtons);
|
|
187
|
+
instructionButton = undefined;
|
|
188
|
+
sections.clear();
|
|
189
|
+
diagnostics = undefined;
|
|
138
190
|
element.hidden = false;
|
|
139
191
|
element.innerHTML = `${dialogHeading('Workspace resources', ids.titleId, ids.dialogId, workspaceName)}<p class="pui-modal-description">Instructions, skills and prompt templates are loaded by Pith for each task. Edit them here or open their Markdown files.</p><div class="pui-feature-toolbar" data-resources-toolbar></div><div id="${escape(ids.listId)}" data-resources-list><p class="pui-feature-hint">Loading workspace resources…</p></div><div id="${escape(ids.errorId)}" data-resources-error class="pui-form-error" role="alert"></div>`;
|
|
140
|
-
element.querySelector('[data-resources-toolbar]')
|
|
192
|
+
const actions = element.querySelector('[data-resources-toolbar]');
|
|
193
|
+
if (toolbar)
|
|
194
|
+
actions.append(toolbar);
|
|
195
|
+
else
|
|
196
|
+
actions.append(button('Refresh', () => options.onRefresh?.()), button('Add skill', () => options.onCreate?.('skill'), false, true), button('Add prompt template', () => options.onCreate?.('template'), false, true));
|
|
141
197
|
list = element.querySelector('[data-resources-list]');
|
|
142
198
|
error = element.querySelector('[data-resources-error]');
|
|
199
|
+
parts.layout(actions, 'toolbar').update({});
|
|
143
200
|
},
|
|
144
201
|
setError(message) { if (!destroyed && error)
|
|
145
|
-
error.
|
|
202
|
+
parts.feedback(error).update({ message, tone: 'error' }); },
|
|
203
|
+
setCreateDisabled(disabled) { if (destroyed)
|
|
204
|
+
return; createDisabled = disabled; for (const [block, label] of createActions)
|
|
205
|
+
block.update({ label, appearance: 'secondary', disabled }); },
|
|
146
206
|
update(state) {
|
|
147
207
|
if (destroyed || !list)
|
|
148
208
|
return;
|
|
149
|
-
|
|
209
|
+
if (!sections.size)
|
|
210
|
+
list.replaceChildren();
|
|
150
211
|
for (const [title, entries, type, empty] of [
|
|
151
212
|
['Instructions', state.instructions, 'instructions', 'No workspace instructions found.'],
|
|
152
213
|
['Skills', state.skills, 'skill', 'No skills found. Add .pi/skills/<skill-name>/SKILL.md in this workspace, then refresh.'],
|
|
153
214
|
['Prompt templates', state.templates, 'template', 'No prompt templates yet.'],
|
|
154
215
|
]) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
216
|
+
let current = sections.get(type);
|
|
217
|
+
if (!current) {
|
|
218
|
+
current = section();
|
|
219
|
+
sections.set(type, current);
|
|
220
|
+
list.append(current.element);
|
|
221
|
+
}
|
|
222
|
+
const items = [{ key: 'heading', kind: 'heading' }, ...entries.map(entry => ({ key: `entry:${entry.path}`, kind: 'entry', entry }))];
|
|
223
|
+
if (!entries.length)
|
|
224
|
+
items.push({ key: 'empty', kind: 'empty' });
|
|
225
|
+
reconcileKeyed(current.element, current.rows, items, {
|
|
226
|
+
key: item => item.key, element: row => row.element,
|
|
227
|
+
create(item) {
|
|
228
|
+
if (item.kind === 'entry')
|
|
229
|
+
return row(type);
|
|
230
|
+
const element = document.createElement(item.kind === 'heading' ? 'h3' : 'p');
|
|
231
|
+
if (item.kind === 'empty')
|
|
232
|
+
element.className = 'pui-feature-hint';
|
|
233
|
+
return { element };
|
|
234
|
+
},
|
|
235
|
+
update(row, item) { if (item.entry)
|
|
236
|
+
updateRow(row, item.entry);
|
|
237
|
+
else
|
|
238
|
+
setText(row.element, item.kind === 'heading' ? title : empty); },
|
|
239
|
+
destroy() { },
|
|
240
|
+
});
|
|
241
|
+
if (type === 'instructions') {
|
|
242
|
+
let create = state.createInstructions;
|
|
243
|
+
if (!create && state.canCreateInstructions) {
|
|
244
|
+
if (!instructionButton) {
|
|
245
|
+
const element = button('Create AGENTS.md in this workspace', () => options.onCreate?.('instructions'), true, true);
|
|
246
|
+
instructionButton = rowButtons.find(block => block.element === element);
|
|
247
|
+
}
|
|
248
|
+
create = instructionButton.element;
|
|
249
|
+
}
|
|
250
|
+
updateSlot(current.element, current.create, create);
|
|
251
|
+
for (const node of current.create.nodes)
|
|
252
|
+
if (node.parentNode !== current.element)
|
|
253
|
+
current.element.append(node);
|
|
167
254
|
}
|
|
168
|
-
if (type === 'instructions' && state.createInstructions)
|
|
169
|
-
section.append(state.createInstructions);
|
|
170
|
-
list.append(section);
|
|
171
255
|
}
|
|
172
256
|
if (state.diagnostics.length) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
257
|
+
if (!diagnostics) {
|
|
258
|
+
diagnostics = section();
|
|
259
|
+
diagnostics.element.classList.add('pui-resource-diagnostics');
|
|
260
|
+
list.append(diagnostics.element);
|
|
261
|
+
}
|
|
262
|
+
reconcileKeyed(diagnostics.element, diagnostics.rows, [{ key: 'heading', text: 'Resource notes' }, ...state.diagnostics.map((text, index) => ({ key: `note:${index}`, text }))], {
|
|
263
|
+
key: value => value.key, element: row => row.element,
|
|
264
|
+
create(value) { return { element: document.createElement(value.key === 'heading' ? 'h3' : 'p') }; },
|
|
265
|
+
update(row, value) { setText(row.element, value.text); }, destroy() { },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else if (diagnostics) {
|
|
269
|
+
diagnostics.element.remove();
|
|
270
|
+
diagnostics = undefined;
|
|
177
271
|
}
|
|
272
|
+
parts.update();
|
|
178
273
|
},
|
|
179
|
-
destroy() {
|
|
274
|
+
destroy() { if (destroyed)
|
|
275
|
+
return; destroyed = true; clearButtons(); clearButtons(rowButtons); sections.clear(); diagnostics = undefined; parts.destroy(); close.destroy(); element.replaceChildren(); },
|
|
180
276
|
};
|
|
181
277
|
}
|
|
182
278
|
export function createFilePreview(options = {}) {
|
|
183
279
|
const element = options.container || document.createElement('div');
|
|
184
280
|
const ids = { titleId: options.titleId || uniqueId('pui-preview-title'), dialogId: options.dialogId || uniqueId('pui-preview-dialog'), bodyId: options.bodyId || uniqueId('pui-preview-body') };
|
|
185
|
-
const close = (
|
|
186
|
-
if (!event.target.closest('[data-close]'))
|
|
187
|
-
return;
|
|
188
|
-
if (options.onClose)
|
|
189
|
-
options.onClose();
|
|
190
|
-
else {
|
|
191
|
-
const dialog = element.closest('dialog');
|
|
192
|
-
if (dialog?.open)
|
|
193
|
-
dialog.close();
|
|
194
|
-
else
|
|
195
|
-
element.hidden = true;
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
element.addEventListener('click', close);
|
|
281
|
+
const close = bindContentClose(element, { dialogId: ids.dialogId, onClose: options.onClose });
|
|
199
282
|
let body;
|
|
200
283
|
let page;
|
|
284
|
+
let image;
|
|
285
|
+
let note;
|
|
286
|
+
let mode;
|
|
201
287
|
let destroyed = false;
|
|
202
|
-
function clear() {
|
|
288
|
+
function clear() {
|
|
289
|
+
if (destroyed)
|
|
290
|
+
return;
|
|
291
|
+
page?.destroy();
|
|
292
|
+
page = undefined;
|
|
293
|
+
image = undefined;
|
|
294
|
+
note = undefined;
|
|
295
|
+
mode = undefined;
|
|
296
|
+
element.replaceChildren();
|
|
297
|
+
body = undefined;
|
|
298
|
+
}
|
|
203
299
|
return {
|
|
204
300
|
element, clear,
|
|
205
301
|
open(input) {
|
|
@@ -214,32 +310,51 @@ export function createFilePreview(options = {}) {
|
|
|
214
310
|
update(state) {
|
|
215
311
|
if (destroyed || !body)
|
|
216
312
|
return;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
313
|
+
const nextMode = state.error ? 'error' : state.kind;
|
|
314
|
+
if (mode !== nextMode) {
|
|
315
|
+
page?.destroy();
|
|
316
|
+
page = undefined;
|
|
317
|
+
image = undefined;
|
|
318
|
+
note = undefined;
|
|
319
|
+
body.replaceChildren();
|
|
320
|
+
mode = nextMode;
|
|
321
|
+
}
|
|
220
322
|
if (state.error)
|
|
221
|
-
body
|
|
323
|
+
setText(body, state.error);
|
|
222
324
|
else if (state.kind === 'image') {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
325
|
+
if (!image) {
|
|
326
|
+
image = document.createElement('img');
|
|
327
|
+
image.className = 'pui-file-preview-image';
|
|
328
|
+
image.setAttribute('role', 'button');
|
|
329
|
+
image.tabIndex = 0;
|
|
330
|
+
image.title = 'Click to enlarge';
|
|
331
|
+
body.append(image);
|
|
332
|
+
}
|
|
333
|
+
if (image.getAttribute('src') !== (state.imageSrc || ''))
|
|
334
|
+
image.src = state.imageSrc || '';
|
|
335
|
+
if (image.alt !== (state.imageAlt || ''))
|
|
336
|
+
image.alt = state.imageAlt || '';
|
|
231
337
|
}
|
|
338
|
+
else if (page)
|
|
339
|
+
page.update({ text: state.text || '', kind: state.kind });
|
|
232
340
|
else
|
|
233
|
-
page = createPagedPreview({ container: body, text: state.text || '', kind: state.kind });
|
|
341
|
+
page = createPagedPreview({ container: body, text: state.text || '', kind: state.kind, onCopyCode: options.onCopyCode });
|
|
234
342
|
if (state.truncated) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
343
|
+
if (!note) {
|
|
344
|
+
note = document.createElement('p');
|
|
345
|
+
note.className = 'pui-preview-note';
|
|
346
|
+
note.textContent = 'Showing the first 8 MiB. Use Open for the complete file.';
|
|
347
|
+
}
|
|
348
|
+
if (note.parentElement !== body)
|
|
349
|
+
body.append(note);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
note?.remove();
|
|
353
|
+
note = undefined;
|
|
239
354
|
}
|
|
240
355
|
},
|
|
241
356
|
destroy() { if (destroyed)
|
|
242
|
-
return; clear();
|
|
357
|
+
return; clear(); close.destroy(); destroyed = true; },
|
|
243
358
|
};
|
|
244
359
|
}
|
|
245
360
|
//# sourceMappingURL=panels.js.map
|