@fgv/ts-app-shell 5.1.0-2 → 5.1.0-21
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/dist/index.browser.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/ai-assist/index.js.map +1 -0
- package/dist/packlets/ai-assist/useAiAssist.js +216 -15
- package/dist/packlets/ai-assist/useAiAssist.js.map +1 -0
- package/dist/packlets/badge/index.js +23 -0
- package/dist/packlets/badge/index.js.map +1 -0
- package/dist/packlets/badge/renderStatusBadge.js +44 -0
- package/dist/packlets/badge/renderStatusBadge.js.map +1 -0
- package/dist/packlets/cascade/CascadeContainer.js.map +1 -0
- package/dist/packlets/cascade/ComparisonView.js.map +1 -0
- package/dist/packlets/cascade/EntityTabLayout.js.map +1 -0
- package/dist/packlets/cascade/MobileCascadeStack.js.map +1 -0
- package/dist/packlets/cascade/index.js.map +1 -0
- package/dist/packlets/cascade/model.js.map +1 -0
- package/dist/packlets/cascade/useCascadeOps.js.map +1 -0
- package/dist/packlets/cascade/useCascadeTransitions.js.map +1 -0
- package/dist/packlets/detail/DetailHelpers.js.map +1 -0
- package/dist/packlets/detail/index.js.map +1 -0
- package/dist/packlets/drop-zone/JsonDropZone.js.map +1 -0
- package/dist/packlets/drop-zone/index.js.map +1 -0
- package/dist/packlets/editing/EditFieldHelpers.js +4 -4
- package/dist/packlets/editing/EditFieldHelpers.js.map +1 -0
- package/dist/packlets/editing/MultiActionButton.js.map +1 -0
- package/dist/packlets/editing/NumericInput.js.map +1 -0
- package/dist/packlets/editing/TypeaheadInput.js.map +1 -0
- package/dist/packlets/editing/index.js.map +1 -0
- package/dist/packlets/editing/useTypeaheadMatch.js.map +1 -0
- package/dist/packlets/keyboard/index.js.map +1 -0
- package/dist/packlets/keyboard/registry.js.map +1 -0
- package/dist/packlets/keyboard/useKeyboardShortcuts.js.map +1 -0
- package/dist/packlets/messages/MessagesContext.js.map +1 -0
- package/dist/packlets/messages/MessagesLogger.js.map +1 -0
- package/dist/packlets/messages/StatusBar.js.map +1 -0
- package/dist/packlets/messages/Toast.js.map +1 -0
- package/dist/packlets/messages/index.js.map +1 -0
- package/dist/packlets/messages/model.js.map +1 -0
- package/dist/packlets/messages/useLogReporter.js.map +1 -0
- package/dist/packlets/modal/ConfirmDialog.js.map +1 -0
- package/dist/packlets/modal/Modal.js.map +1 -0
- package/dist/packlets/modal/index.js.map +1 -0
- package/dist/packlets/print/PrintEnclosure.js.map +1 -0
- package/dist/packlets/print/index.js.map +1 -0
- package/dist/packlets/print/openPrintWindow.js.map +1 -0
- package/dist/packlets/responsive/ResponsiveProvider.js.map +1 -0
- package/dist/packlets/responsive/index.js.map +1 -0
- package/dist/packlets/responsive/useResponsiveLayout.js.map +1 -0
- package/dist/packlets/selectors/EntityRow.js.map +1 -0
- package/dist/packlets/selectors/PreferredSelector.js.map +1 -0
- package/dist/packlets/selectors/index.js.map +1 -0
- package/dist/packlets/sidebar/CollectionSection.js +158 -43
- package/dist/packlets/sidebar/CollectionSection.js.map +1 -0
- package/dist/packlets/sidebar/EntityList.js.map +1 -0
- package/dist/packlets/sidebar/FilterBar.js.map +1 -0
- package/dist/packlets/sidebar/FilterRow.js.map +1 -0
- package/dist/packlets/sidebar/GroupedEntityList.js.map +1 -0
- package/dist/packlets/sidebar/SearchBar.js.map +1 -0
- package/dist/packlets/sidebar/SidebarLayout.js.map +1 -0
- package/dist/packlets/sidebar/index.js.map +1 -0
- package/dist/packlets/theme/ThemeProvider.js.map +1 -0
- package/dist/packlets/theme/index.js.map +1 -0
- package/dist/packlets/top-bar/ModeSelector.js.map +1 -0
- package/dist/packlets/top-bar/TabBar.js +4 -1
- package/dist/packlets/top-bar/TabBar.js.map +1 -0
- package/dist/packlets/top-bar/index.js.map +1 -0
- package/dist/packlets/url-sync/index.js.map +1 -0
- package/dist/packlets/url-sync/useUrlSync.js.map +1 -0
- package/lib/index.browser.d.ts.map +1 -0
- package/lib/index.browser.js.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/ai-assist/index.d.ts.map +1 -0
- package/lib/packlets/ai-assist/index.js.map +1 -0
- package/lib/packlets/ai-assist/useAiAssist.d.ts +26 -0
- package/lib/packlets/ai-assist/useAiAssist.d.ts.map +1 -0
- package/lib/packlets/ai-assist/useAiAssist.js +216 -15
- package/lib/packlets/ai-assist/useAiAssist.js.map +1 -0
- package/lib/packlets/badge/index.d.ts +2 -0
- package/lib/packlets/badge/index.d.ts.map +1 -0
- package/lib/packlets/badge/index.js +27 -0
- package/lib/packlets/badge/index.js.map +1 -0
- package/lib/packlets/badge/renderStatusBadge.d.ts +11 -0
- package/lib/packlets/badge/renderStatusBadge.d.ts.map +1 -0
- package/lib/packlets/badge/renderStatusBadge.js +50 -0
- package/lib/packlets/badge/renderStatusBadge.js.map +1 -0
- package/lib/packlets/cascade/CascadeContainer.d.ts.map +1 -0
- package/lib/packlets/cascade/CascadeContainer.js.map +1 -0
- package/lib/packlets/cascade/ComparisonView.d.ts.map +1 -0
- package/lib/packlets/cascade/ComparisonView.js.map +1 -0
- package/lib/packlets/cascade/EntityTabLayout.d.ts.map +1 -0
- package/lib/packlets/cascade/EntityTabLayout.js.map +1 -0
- package/lib/packlets/cascade/MobileCascadeStack.d.ts.map +1 -0
- package/lib/packlets/cascade/MobileCascadeStack.js.map +1 -0
- package/lib/packlets/cascade/index.d.ts.map +1 -0
- package/lib/packlets/cascade/index.js.map +1 -0
- package/lib/packlets/cascade/model.d.ts.map +1 -0
- package/lib/packlets/cascade/model.js.map +1 -0
- package/lib/packlets/cascade/useCascadeOps.d.ts.map +1 -0
- package/lib/packlets/cascade/useCascadeOps.js.map +1 -0
- package/lib/packlets/cascade/useCascadeTransitions.d.ts.map +1 -0
- package/lib/packlets/cascade/useCascadeTransitions.js.map +1 -0
- package/lib/packlets/detail/DetailHelpers.d.ts.map +1 -0
- package/lib/packlets/detail/DetailHelpers.js.map +1 -0
- package/lib/packlets/detail/index.d.ts.map +1 -0
- package/lib/packlets/detail/index.js.map +1 -0
- package/lib/packlets/drop-zone/JsonDropZone.d.ts.map +1 -0
- package/lib/packlets/drop-zone/JsonDropZone.js.map +1 -0
- package/lib/packlets/drop-zone/index.d.ts.map +1 -0
- package/lib/packlets/drop-zone/index.js.map +1 -0
- package/lib/packlets/editing/EditFieldHelpers.d.ts +6 -2
- package/lib/packlets/editing/EditFieldHelpers.d.ts.map +1 -0
- package/lib/packlets/editing/EditFieldHelpers.js +4 -4
- package/lib/packlets/editing/EditFieldHelpers.js.map +1 -0
- package/lib/packlets/editing/MultiActionButton.d.ts.map +1 -0
- package/lib/packlets/editing/MultiActionButton.js.map +1 -0
- package/lib/packlets/editing/NumericInput.d.ts.map +1 -0
- package/lib/packlets/editing/NumericInput.js.map +1 -0
- package/lib/packlets/editing/TypeaheadInput.d.ts.map +1 -0
- package/lib/packlets/editing/TypeaheadInput.js.map +1 -0
- package/lib/packlets/editing/index.d.ts.map +1 -0
- package/lib/packlets/editing/index.js.map +1 -0
- package/lib/packlets/editing/useTypeaheadMatch.d.ts.map +1 -0
- package/lib/packlets/editing/useTypeaheadMatch.js.map +1 -0
- package/lib/packlets/keyboard/index.d.ts.map +1 -0
- package/lib/packlets/keyboard/index.js.map +1 -0
- package/lib/packlets/keyboard/registry.d.ts.map +1 -0
- package/lib/packlets/keyboard/registry.js.map +1 -0
- package/lib/packlets/keyboard/useKeyboardShortcuts.d.ts.map +1 -0
- package/lib/packlets/keyboard/useKeyboardShortcuts.js.map +1 -0
- package/lib/packlets/messages/MessagesContext.d.ts.map +1 -0
- package/lib/packlets/messages/MessagesContext.js.map +1 -0
- package/lib/packlets/messages/MessagesLogger.d.ts.map +1 -0
- package/lib/packlets/messages/MessagesLogger.js.map +1 -0
- package/lib/packlets/messages/StatusBar.d.ts.map +1 -0
- package/lib/packlets/messages/StatusBar.js.map +1 -0
- package/lib/packlets/messages/Toast.d.ts.map +1 -0
- package/lib/packlets/messages/Toast.js.map +1 -0
- package/lib/packlets/messages/index.d.ts.map +1 -0
- package/lib/packlets/messages/index.js.map +1 -0
- package/lib/packlets/messages/model.d.ts.map +1 -0
- package/lib/packlets/messages/model.js.map +1 -0
- package/lib/packlets/messages/useLogReporter.d.ts.map +1 -0
- package/lib/packlets/messages/useLogReporter.js.map +1 -0
- package/lib/packlets/modal/ConfirmDialog.d.ts.map +1 -0
- package/lib/packlets/modal/ConfirmDialog.js.map +1 -0
- package/lib/packlets/modal/Modal.d.ts.map +1 -0
- package/lib/packlets/modal/Modal.js.map +1 -0
- package/lib/packlets/modal/index.d.ts.map +1 -0
- package/lib/packlets/modal/index.js.map +1 -0
- package/lib/packlets/print/PrintEnclosure.d.ts.map +1 -0
- package/lib/packlets/print/PrintEnclosure.js.map +1 -0
- package/lib/packlets/print/index.d.ts.map +1 -0
- package/lib/packlets/print/index.js.map +1 -0
- package/lib/packlets/print/openPrintWindow.d.ts.map +1 -0
- package/lib/packlets/print/openPrintWindow.js.map +1 -0
- package/lib/packlets/responsive/ResponsiveProvider.d.ts.map +1 -0
- package/lib/packlets/responsive/ResponsiveProvider.js.map +1 -0
- package/lib/packlets/responsive/index.d.ts.map +1 -0
- package/lib/packlets/responsive/index.js.map +1 -0
- package/lib/packlets/responsive/useResponsiveLayout.d.ts.map +1 -0
- package/lib/packlets/responsive/useResponsiveLayout.js.map +1 -0
- package/lib/packlets/selectors/EntityRow.d.ts.map +1 -0
- package/lib/packlets/selectors/EntityRow.js.map +1 -0
- package/lib/packlets/selectors/PreferredSelector.d.ts.map +1 -0
- package/lib/packlets/selectors/PreferredSelector.js.map +1 -0
- package/lib/packlets/selectors/index.d.ts.map +1 -0
- package/lib/packlets/selectors/index.js.map +1 -0
- package/lib/packlets/sidebar/CollectionSection.d.ts +40 -2
- package/lib/packlets/sidebar/CollectionSection.d.ts.map +1 -0
- package/lib/packlets/sidebar/CollectionSection.js +157 -42
- package/lib/packlets/sidebar/CollectionSection.js.map +1 -0
- package/lib/packlets/sidebar/EntityList.d.ts.map +1 -0
- package/lib/packlets/sidebar/EntityList.js.map +1 -0
- package/lib/packlets/sidebar/FilterBar.d.ts.map +1 -0
- package/lib/packlets/sidebar/FilterBar.js.map +1 -0
- package/lib/packlets/sidebar/FilterRow.d.ts.map +1 -0
- package/lib/packlets/sidebar/FilterRow.js.map +1 -0
- package/lib/packlets/sidebar/GroupedEntityList.d.ts.map +1 -0
- package/lib/packlets/sidebar/GroupedEntityList.js.map +1 -0
- package/lib/packlets/sidebar/SearchBar.d.ts.map +1 -0
- package/lib/packlets/sidebar/SearchBar.js.map +1 -0
- package/lib/packlets/sidebar/SidebarLayout.d.ts.map +1 -0
- package/lib/packlets/sidebar/SidebarLayout.js.map +1 -0
- package/lib/packlets/sidebar/index.d.ts +1 -1
- package/lib/packlets/sidebar/index.d.ts.map +1 -0
- package/lib/packlets/sidebar/index.js.map +1 -0
- package/lib/packlets/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/packlets/theme/ThemeProvider.js.map +1 -0
- package/lib/packlets/theme/index.d.ts.map +1 -0
- package/lib/packlets/theme/index.js.map +1 -0
- package/lib/packlets/top-bar/ModeSelector.d.ts.map +1 -0
- package/lib/packlets/top-bar/ModeSelector.js.map +1 -0
- package/lib/packlets/top-bar/TabBar.d.ts +12 -0
- package/lib/packlets/top-bar/TabBar.d.ts.map +1 -0
- package/lib/packlets/top-bar/TabBar.js +4 -1
- package/lib/packlets/top-bar/TabBar.js.map +1 -0
- package/lib/packlets/top-bar/index.d.ts +1 -1
- package/lib/packlets/top-bar/index.d.ts.map +1 -0
- package/lib/packlets/top-bar/index.js.map +1 -0
- package/lib/packlets/url-sync/index.d.ts.map +1 -0
- package/lib/packlets/url-sync/index.js.map +1 -0
- package/lib/packlets/url-sync/useUrlSync.d.ts.map +1 -0
- package/lib/packlets/url-sync/useUrlSync.js.map +1 -0
- package/package.json +16 -16
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
* SOFTWARE.
|
|
22
22
|
*/
|
|
23
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
24
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
25
|
+
var m = o[Symbol.asyncIterator], i;
|
|
26
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
27
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
28
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
29
|
+
};
|
|
23
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
31
|
exports.checkForAiErrorObject = checkForAiErrorObject;
|
|
25
32
|
exports.useAiAssist = useAiAssist;
|
|
@@ -37,6 +44,35 @@ const ts_utils_1 = require("@fgv/ts-utils");
|
|
|
37
44
|
// ============================================================================
|
|
38
45
|
// Helpers
|
|
39
46
|
// ============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Resolves the API key for a dispatcher call, honoring `descriptor.needsSecret`.
|
|
49
|
+
*
|
|
50
|
+
* - When `descriptor.needsSecret === false` (e.g. ollama, openai-compat),
|
|
51
|
+
* returns the configured key if one happens to be set in the keystore, or
|
|
52
|
+
* an empty string otherwise. ts-extras omits the `Authorization` header
|
|
53
|
+
* entirely for empty keys, which self-hosted servers expect.
|
|
54
|
+
* - When `descriptor.needsSecret !== false`, requires `secretName`, an
|
|
55
|
+
* unlocked keystore, and a successful key fetch.
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
function resolveApiKey(descriptor, providerConfig, keyStore) {
|
|
60
|
+
var _a;
|
|
61
|
+
const { provider, secretName } = providerConfig;
|
|
62
|
+
if (descriptor.needsSecret === false) {
|
|
63
|
+
if (!secretName || !(keyStore === null || keyStore === void 0 ? void 0 : keyStore.isUnlocked)) {
|
|
64
|
+
return (0, ts_utils_1.succeed)('');
|
|
65
|
+
}
|
|
66
|
+
return (0, ts_utils_1.succeed)((_a = keyStore.getApiKey(secretName).orDefault('')) !== null && _a !== void 0 ? _a : '');
|
|
67
|
+
}
|
|
68
|
+
if (!secretName) {
|
|
69
|
+
return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
|
|
70
|
+
}
|
|
71
|
+
if (!keyStore) {
|
|
72
|
+
return (0, ts_utils_1.fail)('No keystore available');
|
|
73
|
+
}
|
|
74
|
+
return keyStore.getApiKey(secretName).withErrorFormat((m) => `Failed to get API key: ${m}`);
|
|
75
|
+
}
|
|
40
76
|
/**
|
|
41
77
|
* Checks whether a parsed AI response is an error object (with an "error" field)
|
|
42
78
|
* rather than a valid entity. AI prompts instruct the model to return
|
|
@@ -76,11 +112,15 @@ function useAiAssist(params) {
|
|
|
76
112
|
? settings.defaultProvider
|
|
77
113
|
: (_c = (_b = providers[0]) === null || _b === void 0 ? void 0 : _b.provider) !== null && _c !== void 0 ? _c : 'copy-paste';
|
|
78
114
|
return providers.map((config) => {
|
|
79
|
-
var _a
|
|
115
|
+
var _a;
|
|
80
116
|
let isAvailable = true;
|
|
81
117
|
let unavailableReason;
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
const descriptor = ts_extras_1.AiAssist.getProviderDescriptor(config.provider).orDefault();
|
|
119
|
+
// Self-hosted / no-secret providers (e.g. ollama, openai-compat) skip
|
|
120
|
+
// the keystore check entirely — they're available as long as the
|
|
121
|
+
// descriptor exists. Providers with `needsSecret: true` still require
|
|
122
|
+
// a configured secretName and an unlocked keystore.
|
|
123
|
+
if (config.provider !== 'copy-paste' && (descriptor === null || descriptor === void 0 ? void 0 : descriptor.needsSecret) !== false) {
|
|
84
124
|
if (!config.secretName) {
|
|
85
125
|
isAvailable = false;
|
|
86
126
|
unavailableReason = 'No API key secret configured';
|
|
@@ -101,7 +141,7 @@ function useAiAssist(params) {
|
|
|
101
141
|
}
|
|
102
142
|
}
|
|
103
143
|
}
|
|
104
|
-
const label = (
|
|
144
|
+
const label = (_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.buttonLabel) !== null && _a !== void 0 ? _a : config.provider;
|
|
105
145
|
return {
|
|
106
146
|
provider: config.provider,
|
|
107
147
|
label,
|
|
@@ -131,16 +171,9 @@ function useAiAssist(params) {
|
|
|
131
171
|
return (0, ts_utils_1.fail)(descriptorResult.message);
|
|
132
172
|
}
|
|
133
173
|
const descriptor = descriptorResult.value;
|
|
134
|
-
|
|
135
|
-
return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
|
|
136
|
-
}
|
|
137
|
-
if (!keyStore) {
|
|
138
|
-
return (0, ts_utils_1.fail)('No keystore available');
|
|
139
|
-
}
|
|
140
|
-
// Get API key from keystore
|
|
141
|
-
const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
|
|
174
|
+
const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
|
|
142
175
|
if (apiKeyResult.isFailure()) {
|
|
143
|
-
return (0, ts_utils_1.fail)(
|
|
176
|
+
return (0, ts_utils_1.fail)(apiKeyResult.message);
|
|
144
177
|
}
|
|
145
178
|
setIsWorking(true);
|
|
146
179
|
try {
|
|
@@ -156,6 +189,7 @@ function useAiAssist(params) {
|
|
|
156
189
|
prompt,
|
|
157
190
|
additionalMessages: correctionMessages.length > 0 ? correctionMessages : undefined,
|
|
158
191
|
modelOverride: providerConfig.model,
|
|
192
|
+
endpoint: providerConfig.endpoint,
|
|
159
193
|
logger,
|
|
160
194
|
tools: effectiveTools.length > 0 ? effectiveTools : undefined
|
|
161
195
|
};
|
|
@@ -211,13 +245,180 @@ function useAiAssist(params) {
|
|
|
211
245
|
return (0, ts_utils_1.fail)(`AI response validation failed after ${maxAttempts} attempts: ${entityResult.message}`);
|
|
212
246
|
}
|
|
213
247
|
}
|
|
214
|
-
|
|
248
|
+
/* c8 ignore next 2 - unreachable: every loop iteration returns; TypeScript needs the trailing return */
|
|
215
249
|
return (0, ts_utils_1.fail)('AI generation failed');
|
|
216
250
|
}
|
|
217
251
|
finally {
|
|
218
252
|
setIsWorking(false);
|
|
219
253
|
}
|
|
220
254
|
}, [settings, keyStore, logger]);
|
|
221
|
-
|
|
255
|
+
const generateImages = (0, react_1.useCallback)(async (provider, params, signal) => {
|
|
256
|
+
const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
|
|
257
|
+
if (!providerConfig) {
|
|
258
|
+
return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
|
|
259
|
+
}
|
|
260
|
+
const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
|
|
261
|
+
if (descriptorResult.isFailure()) {
|
|
262
|
+
return (0, ts_utils_1.fail)(descriptorResult.message);
|
|
263
|
+
}
|
|
264
|
+
const descriptor = descriptorResult.value;
|
|
265
|
+
if (!ts_extras_1.AiAssist.supportsImageGeneration(descriptor)) {
|
|
266
|
+
return (0, ts_utils_1.fail)(`Provider "${provider}" does not support image generation`);
|
|
267
|
+
}
|
|
268
|
+
const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
|
|
269
|
+
if (apiKeyResult.isFailure()) {
|
|
270
|
+
return (0, ts_utils_1.fail)(apiKeyResult.message);
|
|
271
|
+
}
|
|
272
|
+
setIsWorking(true);
|
|
273
|
+
try {
|
|
274
|
+
const requestParams = {
|
|
275
|
+
descriptor,
|
|
276
|
+
apiKey: apiKeyResult.value,
|
|
277
|
+
params,
|
|
278
|
+
modelOverride: providerConfig.model,
|
|
279
|
+
endpoint: providerConfig.endpoint,
|
|
280
|
+
logger,
|
|
281
|
+
signal
|
|
282
|
+
};
|
|
283
|
+
const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
|
|
284
|
+
const result = useProxy
|
|
285
|
+
? await ts_extras_1.AiAssist.callProxiedImageGeneration(settings.proxyUrl, requestParams)
|
|
286
|
+
: await ts_extras_1.AiAssist.callProviderImageGeneration(requestParams);
|
|
287
|
+
if (result.isFailure()) {
|
|
288
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`AI image generation failed: ${result.message}`);
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
setIsWorking(false);
|
|
294
|
+
}
|
|
295
|
+
}, [settings, keyStore, logger]);
|
|
296
|
+
const listModels = (0, react_1.useCallback)(async (provider, capability, signal) => {
|
|
297
|
+
const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
|
|
298
|
+
if (!providerConfig) {
|
|
299
|
+
return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
|
|
300
|
+
}
|
|
301
|
+
const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
|
|
302
|
+
if (descriptorResult.isFailure()) {
|
|
303
|
+
return (0, ts_utils_1.fail)(descriptorResult.message);
|
|
304
|
+
}
|
|
305
|
+
const descriptor = descriptorResult.value;
|
|
306
|
+
const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
|
|
307
|
+
if (apiKeyResult.isFailure()) {
|
|
308
|
+
return (0, ts_utils_1.fail)(apiKeyResult.message);
|
|
309
|
+
}
|
|
310
|
+
const requestParams = Object.assign(Object.assign({ descriptor, apiKey: apiKeyResult.value }, (capability !== undefined ? { capability } : {})), { endpoint: providerConfig.endpoint, logger,
|
|
311
|
+
signal });
|
|
312
|
+
const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
|
|
313
|
+
const result = useProxy
|
|
314
|
+
? await ts_extras_1.AiAssist.callProxiedListModels(settings.proxyUrl, requestParams)
|
|
315
|
+
: await ts_extras_1.AiAssist.callProviderListModels(requestParams);
|
|
316
|
+
if (result.isFailure()) {
|
|
317
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`AI list-models failed: ${result.message}`);
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}, [settings, keyStore, logger]);
|
|
321
|
+
const streamDirect = (0, react_1.useCallback)(async (provider, prompt, onEvent, options) => {
|
|
322
|
+
var _a, e_1, _b, _c;
|
|
323
|
+
const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
|
|
324
|
+
if (!providerConfig) {
|
|
325
|
+
return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
|
|
326
|
+
}
|
|
327
|
+
const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
|
|
328
|
+
if (descriptorResult.isFailure()) {
|
|
329
|
+
return (0, ts_utils_1.fail)(descriptorResult.message);
|
|
330
|
+
}
|
|
331
|
+
const descriptor = descriptorResult.value;
|
|
332
|
+
const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);
|
|
333
|
+
if (apiKeyResult.isFailure()) {
|
|
334
|
+
return (0, ts_utils_1.fail)(apiKeyResult.message);
|
|
335
|
+
}
|
|
336
|
+
const effectiveTools = ts_extras_1.AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, options === null || options === void 0 ? void 0 : options.tools);
|
|
337
|
+
const requestParams = {
|
|
338
|
+
descriptor,
|
|
339
|
+
apiKey: apiKeyResult.value,
|
|
340
|
+
prompt,
|
|
341
|
+
messagesBefore: options === null || options === void 0 ? void 0 : options.messagesBefore,
|
|
342
|
+
modelOverride: providerConfig.model,
|
|
343
|
+
endpoint: providerConfig.endpoint,
|
|
344
|
+
logger,
|
|
345
|
+
tools: effectiveTools.length > 0 ? effectiveTools : undefined,
|
|
346
|
+
signal: options === null || options === void 0 ? void 0 : options.signal
|
|
347
|
+
};
|
|
348
|
+
const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.streamingCorsRestricted);
|
|
349
|
+
const openResult = useProxy
|
|
350
|
+
? await ts_extras_1.AiAssist.callProxiedCompletionStream(settings.proxyUrl, requestParams)
|
|
351
|
+
: await ts_extras_1.AiAssist.callProviderCompletionStream(requestParams);
|
|
352
|
+
if (openResult.isFailure()) {
|
|
353
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming failed to start: ${openResult.message}`);
|
|
354
|
+
return (0, ts_utils_1.fail)(openResult.message);
|
|
355
|
+
}
|
|
356
|
+
setIsWorking(true);
|
|
357
|
+
let fullText = '';
|
|
358
|
+
let truncated = false;
|
|
359
|
+
let terminalError;
|
|
360
|
+
let receivedTerminalEvent = false;
|
|
361
|
+
try {
|
|
362
|
+
try {
|
|
363
|
+
for (var _d = true, _e = __asyncValues(openResult.value), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
|
|
364
|
+
_c = _f.value;
|
|
365
|
+
_d = false;
|
|
366
|
+
const event = _c;
|
|
367
|
+
try {
|
|
368
|
+
onEvent(event);
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
// Consumer callback threw (e.g. setState-after-unmount). Convert
|
|
372
|
+
// into a terminal failure so streamDirect always resolves to a
|
|
373
|
+
// Result instead of rejecting and breaking its contract.
|
|
374
|
+
terminalError = `AI stream event handler failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
375
|
+
receivedTerminalEvent = true;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
if (event.type === 'text-delta') {
|
|
379
|
+
fullText += event.delta;
|
|
380
|
+
}
|
|
381
|
+
else if (event.type === 'done') {
|
|
382
|
+
fullText = event.fullText;
|
|
383
|
+
truncated = event.truncated;
|
|
384
|
+
receivedTerminalEvent = true;
|
|
385
|
+
}
|
|
386
|
+
else if (event.type === 'error') {
|
|
387
|
+
terminalError = event.message;
|
|
388
|
+
receivedTerminalEvent = true;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
393
|
+
finally {
|
|
394
|
+
try {
|
|
395
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
396
|
+
}
|
|
397
|
+
finally { if (e_1) throw e_1.error; }
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
setIsWorking(false);
|
|
402
|
+
}
|
|
403
|
+
if (terminalError !== undefined) {
|
|
404
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming ended in error: ${terminalError}`);
|
|
405
|
+
return (0, ts_utils_1.fail)(terminalError);
|
|
406
|
+
}
|
|
407
|
+
if (!receivedTerminalEvent) {
|
|
408
|
+
const message = 'AI stream ended without a terminal done or error event';
|
|
409
|
+
logger === null || logger === void 0 ? void 0 : logger.error(message);
|
|
410
|
+
return (0, ts_utils_1.fail)(message);
|
|
411
|
+
}
|
|
412
|
+
return (0, ts_utils_1.succeed)({ fullText, truncated });
|
|
413
|
+
}, [settings, keyStore, logger]);
|
|
414
|
+
return {
|
|
415
|
+
actions,
|
|
416
|
+
isWorking,
|
|
417
|
+
copyPrompt,
|
|
418
|
+
generateDirect,
|
|
419
|
+
generateImages,
|
|
420
|
+
listModels,
|
|
421
|
+
streamDirect
|
|
422
|
+
};
|
|
222
423
|
}
|
|
223
424
|
//# sourceMappingURL=useAiAssist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiAssist.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/useAiAssist.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Generic AI assist hook — parameterized by settings and keystore, no app-specific context.\n *\n * Provides a list of available AI assist actions and functions for copy-paste\n * and direct API generation flows.\n *\n * @packageDocumentation\n */\n\nimport { useCallback, useMemo, useState } from 'react';\n\nimport { AiAssist } from '@fgv/ts-extras';\nimport { fail, type Logging, Result, succeed } from '@fgv/ts-utils';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Parameters for the generic useAiAssist hook.\n * @public\n */\nexport interface IUseAiAssistParams {\n /** Resolved AI assist settings (provider list + default). */\n readonly settings: AiAssist.IAiAssistSettings | undefined;\n /** Keystore for API key resolution (or undefined if no keystore). */\n readonly keyStore: AiAssist.IAiAssistKeyStore | undefined;\n /** Optional logger for request/response observability. */\n readonly logger?: Logging.ILogger;\n}\n\n/**\n * A single available AI assist action.\n * @public\n */\nexport interface IAiAssistAction {\n /** The provider identifier */\n readonly provider: AiAssist.AiProviderId;\n /** Display label (e.g. \"AI Assist | Copy\", \"AI Assist | Grok\") */\n readonly label: string;\n /** Whether this is the default (first) action */\n readonly isDefault: boolean;\n /** Whether the provider is currently available (keystore unlocked, secret present) */\n readonly isAvailable: boolean;\n /** Reason the provider is unavailable, if any */\n readonly unavailableReason?: string;\n}\n\n/**\n * Result of executing an AI assist action.\n * @public\n */\nexport interface IAiAssistResult<TEntity> {\n /** The generated entity */\n readonly entity: TEntity;\n /** Source indicator */\n readonly source: 'ai';\n}\n\n/**\n * Return type of the useAiAssist hook.\n * @public\n */\nexport interface IUseAiAssistResult {\n /** Available actions based on settings + keystore state */\n readonly actions: ReadonlyArray<IAiAssistAction>;\n /** Whether a direct assist call is in progress */\n readonly isWorking: boolean;\n /**\n * Execute a copy-paste action: copies the combined prompt to clipboard.\n * @returns Success with `'copied'`, or failure.\n */\n readonly copyPrompt: (prompt: AiAssist.AiPrompt) => Promise<Result<'copied'>>;\n /**\n * Execute a direct API action: calls the provider, validates the response, returns the entity.\n * @returns Success with the validated entity, or failure.\n */\n readonly generateDirect: <TEntity>(\n provider: AiAssist.AiProviderId,\n prompt: AiAssist.AiPrompt,\n convert: (from: unknown) => Result<TEntity>,\n tools?: ReadonlyArray<AiAssist.AiServerToolConfig>\n ) => Promise<Result<IAiAssistResult<TEntity>>>;\n /**\n * Execute an image-generation action: calls the provider's image API and\n * returns the generated images.\n * @returns Success with the image generation response, or failure.\n */\n readonly generateImages: (\n provider: AiAssist.AiProviderId,\n params: AiAssist.IAiImageGenerationParams,\n signal?: AbortSignal\n ) => Promise<Result<AiAssist.IAiImageGenerationResponse>>;\n /**\n * List the models available from a provider, optionally filtered by capability.\n * @returns Success with the resolved model list, or failure (no silent fallback).\n */\n readonly listModels: (\n provider: AiAssist.AiProviderId,\n capability?: AiAssist.AiModelCapability,\n signal?: AbortSignal\n ) => Promise<Result<ReadonlyArray<AiAssist.IAiModelInfo>>>;\n /**\n * Stream a chat completion from the provider. The `onEvent` callback fires\n * for every event (text deltas, tool progress, errors); the returned\n * promise resolves with the aggregated final text and truncation flag on\n * success, or with `Result.fail` when the stream couldn't be started or\n * ended in a terminal error event.\n */\n readonly streamDirect: (\n provider: AiAssist.AiProviderId,\n prompt: AiAssist.AiPrompt,\n onEvent: (event: AiAssist.IAiStreamEvent) => void,\n options?: {\n readonly tools?: ReadonlyArray<AiAssist.AiServerToolConfig>;\n readonly messagesBefore?: ReadonlyArray<AiAssist.IChatMessage>;\n readonly signal?: AbortSignal;\n }\n ) => Promise<Result<{ readonly fullText: string; readonly truncated: boolean }>>;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Resolves the API key for a dispatcher call, honoring `descriptor.needsSecret`.\n *\n * - When `descriptor.needsSecret === false` (e.g. ollama, openai-compat),\n * returns the configured key if one happens to be set in the keystore, or\n * an empty string otherwise. ts-extras omits the `Authorization` header\n * entirely for empty keys, which self-hosted servers expect.\n * - When `descriptor.needsSecret !== false`, requires `secretName`, an\n * unlocked keystore, and a successful key fetch.\n *\n * @internal\n */\nfunction resolveApiKey(\n descriptor: AiAssist.IAiProviderDescriptor,\n providerConfig: AiAssist.IAiAssistProviderConfig,\n keyStore: AiAssist.IAiAssistKeyStore | undefined\n): Result<string> {\n const { provider, secretName } = providerConfig;\n if (descriptor.needsSecret === false) {\n if (!secretName || !keyStore?.isUnlocked) {\n return succeed('');\n }\n return succeed(keyStore.getApiKey(secretName).orDefault('') ?? '');\n }\n if (!secretName) {\n return fail(`Provider \"${provider}\" has no secret name configured`);\n }\n if (!keyStore) {\n return fail('No keystore available');\n }\n return keyStore.getApiKey(secretName).withErrorFormat((m) => `Failed to get API key: ${m}`);\n}\n\n/**\n * Checks whether a parsed AI response is an error object (with an \"error\" field)\n * rather than a valid entity. AI prompts instruct the model to return\n * `{ \"error\": \"...\", \"term\": \"...\" }` when it cannot confidently generate the entity.\n *\n * @param parsed - The parsed JSON from the AI response\n * @returns A failure Result with the error message if it's an error object, or undefined if not\n */\nexport function checkForAiErrorObject(parsed: unknown): Result<never> | undefined {\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'error' in parsed &&\n typeof (parsed as Record<string, unknown>).error === 'string'\n ) {\n const errorObj = parsed as Record<string, unknown>;\n const term = typeof errorObj.term === 'string' ? ` (term: \"${errorObj.term}\")` : '';\n return fail(`AI declined to generate${term}: ${errorObj.error}`);\n }\n return undefined;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Generic hook providing AI assist actions based on supplied settings and keystore.\n * @param params - Settings and keystore to use (typically from a workspace or app context)\n * @returns Available actions and execution functions\n * @public\n */\nexport function useAiAssist(params: IUseAiAssistParams): IUseAiAssistResult {\n const { settings, keyStore, logger } = params;\n const [isWorking, setIsWorking] = useState(false);\n\n const actions = useMemo((): ReadonlyArray<IAiAssistAction> => {\n const providers = settings?.providers ?? [{ provider: 'copy-paste' as AiAssist.AiProviderId }];\n const enabledSet = new Set(providers.map((p) => p.provider));\n const defaultProvider: AiAssist.AiProviderId =\n settings?.defaultProvider && enabledSet.has(settings.defaultProvider)\n ? settings.defaultProvider\n : providers[0]?.provider ?? ('copy-paste' as AiAssist.AiProviderId);\n\n return providers.map((config) => {\n let isAvailable = true;\n let unavailableReason: string | undefined;\n\n const descriptor = AiAssist.getProviderDescriptor(config.provider).orDefault();\n\n // Self-hosted / no-secret providers (e.g. ollama, openai-compat) skip\n // the keystore check entirely — they're available as long as the\n // descriptor exists. Providers with `needsSecret: true` still require\n // a configured secretName and an unlocked keystore.\n if (config.provider !== 'copy-paste' && descriptor?.needsSecret !== false) {\n if (!config.secretName) {\n isAvailable = false;\n unavailableReason = 'No API key secret configured';\n } else if (!keyStore) {\n isAvailable = false;\n unavailableReason = 'No keystore available';\n } else if (!keyStore.isUnlocked) {\n isAvailable = false;\n unavailableReason = 'Keystore is locked';\n } else {\n const hasSecret = keyStore.hasSecret(config.secretName);\n if (hasSecret.isFailure() || !hasSecret.value) {\n isAvailable = false;\n unavailableReason = `API key \"${config.secretName}\" not found in keystore`;\n }\n }\n }\n\n const label = descriptor?.buttonLabel ?? config.provider;\n return {\n provider: config.provider,\n label,\n isDefault: config.provider === defaultProvider,\n isAvailable,\n unavailableReason\n };\n });\n }, [settings, keyStore]);\n\n const copyPrompt = useCallback(async (prompt: AiAssist.AiPrompt): Promise<Result<'copied'>> => {\n try {\n await navigator.clipboard.writeText(prompt.combined);\n return succeed('copied' as const);\n } catch {\n return fail('Failed to copy prompt to clipboard');\n }\n }, []);\n\n const generateDirect = useCallback(\n async <TEntity>(\n provider: AiAssist.AiProviderId,\n prompt: AiAssist.AiPrompt,\n convert: (from: unknown) => Result<TEntity>,\n tools?: ReadonlyArray<AiAssist.AiServerToolConfig>\n ): Promise<Result<IAiAssistResult<TEntity>>> => {\n // Find the provider config and descriptor\n const providerConfig = settings?.providers.find((p) => p.provider === provider);\n\n if (!providerConfig) {\n return fail(`Provider \"${provider}\" not configured`);\n }\n\n const descriptorResult = AiAssist.getProviderDescriptor(provider);\n if (descriptorResult.isFailure()) {\n return fail(descriptorResult.message);\n }\n const descriptor = descriptorResult.value;\n\n const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);\n if (apiKeyResult.isFailure()) {\n return fail(apiKeyResult.message);\n }\n\n setIsWorking(true);\n try {\n const maxAttempts = 3;\n const correctionMessages: Array<AiAssist.IChatMessage> = [];\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n // Resolve effective tools: per-call override > settings defaults > none\n const effectiveTools = AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, tools);\n\n // Call the API — through proxy if configured, otherwise direct\n const completionParams: AiAssist.IProviderCompletionParams = {\n descriptor,\n apiKey: apiKeyResult.value,\n prompt,\n additionalMessages: correctionMessages.length > 0 ? correctionMessages : undefined,\n modelOverride: providerConfig.model,\n endpoint: providerConfig.endpoint,\n logger,\n tools: effectiveTools.length > 0 ? effectiveTools : undefined\n };\n const useProxy: boolean =\n !!settings?.proxyUrl && (settings.proxyAllProviders === true || descriptor.corsRestricted);\n const responseResult = useProxy\n ? await AiAssist.callProxiedCompletion(settings!.proxyUrl!, completionParams)\n : await AiAssist.callProviderCompletion(completionParams);\n if (responseResult.isFailure()) {\n logger?.error(`AI completion failed: ${responseResult.message}`);\n return fail(responseResult.message);\n }\n\n const { content: rawResponse, truncated } = responseResult.value;\n logger?.detail(`AI response received (${rawResponse.length} chars, truncated=${truncated})`);\n\n // Truncated responses are almost certainly malformed JSON — fail early\n if (truncated) {\n logger?.warn('AI response truncated due to token limits');\n return fail('AI response was truncated due to token limits — try a shorter prompt');\n }\n\n // Strip markdown code fences and parse JSON\n const stripped = rawResponse\n .trim()\n .replace(/^```(?:\\w+)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```$/, '$1')\n .trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripped);\n } catch (err: unknown) {\n const detail = err instanceof Error ? err.message : String(err);\n // JSON parse failures are not retryable — the model isn't producing valid JSON at all\n return fail(`AI returned invalid JSON: ${detail}`);\n }\n\n // Check for AI error object (model declined to generate)\n const aiError = checkForAiErrorObject(parsed);\n if (aiError !== undefined) {\n return aiError;\n }\n\n // Validate with the provided converter\n const entityResult = convert(parsed);\n if (entityResult.isSuccess()) {\n return succeed({ entity: entityResult.value, source: 'ai' as const });\n }\n\n // Validation failed — if we have retries left, send a correction\n logger?.warn(\n `AI response validation failed (attempt ${attempt + 1}/${maxAttempts}): ${entityResult.message}`\n );\n if (attempt < maxAttempts - 1) {\n correctionMessages.push(\n { role: 'assistant', content: rawResponse },\n {\n role: 'user',\n content: `The JSON you returned failed validation with the following error:\\n\\n${entityResult.message}\\n\\nPlease fix the JSON and return ONLY the corrected JSON object, nothing else.`\n }\n );\n } else {\n // Out of retries\n return fail(\n `AI response validation failed after ${maxAttempts} attempts: ${entityResult.message}`\n );\n }\n }\n\n /* c8 ignore next 2 - unreachable: every loop iteration returns; TypeScript needs the trailing return */\n return fail('AI generation failed');\n } finally {\n setIsWorking(false);\n }\n },\n [settings, keyStore, logger]\n );\n\n const generateImages = useCallback(\n async (\n provider: AiAssist.AiProviderId,\n params: AiAssist.IAiImageGenerationParams,\n signal?: AbortSignal\n ): Promise<Result<AiAssist.IAiImageGenerationResponse>> => {\n const providerConfig = settings?.providers.find((p) => p.provider === provider);\n if (!providerConfig) {\n return fail(`Provider \"${provider}\" not configured`);\n }\n\n const descriptorResult = AiAssist.getProviderDescriptor(provider);\n if (descriptorResult.isFailure()) {\n return fail(descriptorResult.message);\n }\n const descriptor = descriptorResult.value;\n\n if (!AiAssist.supportsImageGeneration(descriptor)) {\n return fail(`Provider \"${provider}\" does not support image generation`);\n }\n\n const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);\n if (apiKeyResult.isFailure()) {\n return fail(apiKeyResult.message);\n }\n\n setIsWorking(true);\n try {\n const requestParams: AiAssist.IProviderImageGenerationParams = {\n descriptor,\n apiKey: apiKeyResult.value,\n params,\n modelOverride: providerConfig.model,\n endpoint: providerConfig.endpoint,\n logger,\n signal\n };\n const useProxy: boolean =\n !!settings?.proxyUrl && (settings.proxyAllProviders === true || descriptor.corsRestricted);\n const result = useProxy\n ? await AiAssist.callProxiedImageGeneration(settings!.proxyUrl!, requestParams)\n : await AiAssist.callProviderImageGeneration(requestParams);\n if (result.isFailure()) {\n logger?.error(`AI image generation failed: ${result.message}`);\n }\n return result;\n } finally {\n setIsWorking(false);\n }\n },\n [settings, keyStore, logger]\n );\n\n const listModels = useCallback(\n async (\n provider: AiAssist.AiProviderId,\n capability?: AiAssist.AiModelCapability,\n signal?: AbortSignal\n ): Promise<Result<ReadonlyArray<AiAssist.IAiModelInfo>>> => {\n const providerConfig = settings?.providers.find((p) => p.provider === provider);\n if (!providerConfig) {\n return fail(`Provider \"${provider}\" not configured`);\n }\n\n const descriptorResult = AiAssist.getProviderDescriptor(provider);\n if (descriptorResult.isFailure()) {\n return fail(descriptorResult.message);\n }\n const descriptor = descriptorResult.value;\n\n const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);\n if (apiKeyResult.isFailure()) {\n return fail(apiKeyResult.message);\n }\n\n const requestParams: AiAssist.IProviderListModelsParams = {\n descriptor,\n apiKey: apiKeyResult.value,\n ...(capability !== undefined ? { capability } : {}),\n endpoint: providerConfig.endpoint,\n logger,\n signal\n };\n const useProxy: boolean =\n !!settings?.proxyUrl && (settings.proxyAllProviders === true || descriptor.corsRestricted);\n const result = useProxy\n ? await AiAssist.callProxiedListModels(settings!.proxyUrl!, requestParams)\n : await AiAssist.callProviderListModels(requestParams);\n if (result.isFailure()) {\n logger?.error(`AI list-models failed: ${result.message}`);\n }\n return result;\n },\n [settings, keyStore, logger]\n );\n\n const streamDirect = useCallback(\n async (\n provider: AiAssist.AiProviderId,\n prompt: AiAssist.AiPrompt,\n onEvent: (event: AiAssist.IAiStreamEvent) => void,\n options?: {\n readonly tools?: ReadonlyArray<AiAssist.AiServerToolConfig>;\n readonly messagesBefore?: ReadonlyArray<AiAssist.IChatMessage>;\n readonly signal?: AbortSignal;\n }\n ): Promise<Result<{ readonly fullText: string; readonly truncated: boolean }>> => {\n const providerConfig = settings?.providers.find((p) => p.provider === provider);\n if (!providerConfig) {\n return fail(`Provider \"${provider}\" not configured`);\n }\n\n const descriptorResult = AiAssist.getProviderDescriptor(provider);\n if (descriptorResult.isFailure()) {\n return fail(descriptorResult.message);\n }\n const descriptor = descriptorResult.value;\n\n const apiKeyResult = resolveApiKey(descriptor, providerConfig, keyStore);\n if (apiKeyResult.isFailure()) {\n return fail(apiKeyResult.message);\n }\n\n const effectiveTools = AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, options?.tools);\n const requestParams: AiAssist.IProviderCompletionStreamParams = {\n descriptor,\n apiKey: apiKeyResult.value,\n prompt,\n messagesBefore: options?.messagesBefore,\n modelOverride: providerConfig.model,\n endpoint: providerConfig.endpoint,\n logger,\n tools: effectiveTools.length > 0 ? effectiveTools : undefined,\n signal: options?.signal\n };\n const useProxy: boolean =\n !!settings?.proxyUrl && (settings.proxyAllProviders === true || descriptor.streamingCorsRestricted);\n const openResult = useProxy\n ? await AiAssist.callProxiedCompletionStream(settings!.proxyUrl!, requestParams)\n : await AiAssist.callProviderCompletionStream(requestParams);\n if (openResult.isFailure()) {\n logger?.error(`AI streaming failed to start: ${openResult.message}`);\n return fail(openResult.message);\n }\n\n setIsWorking(true);\n let fullText = '';\n let truncated = false;\n let terminalError: string | undefined;\n let receivedTerminalEvent = false;\n try {\n for await (const event of openResult.value) {\n try {\n onEvent(event);\n } catch (err: unknown) {\n // Consumer callback threw (e.g. setState-after-unmount). Convert\n // into a terminal failure so streamDirect always resolves to a\n // Result instead of rejecting and breaking its contract.\n terminalError = `AI stream event handler failed: ${\n err instanceof Error ? err.message : String(err)\n }`;\n receivedTerminalEvent = true;\n break;\n }\n if (event.type === 'text-delta') {\n fullText += event.delta;\n } else if (event.type === 'done') {\n fullText = event.fullText;\n truncated = event.truncated;\n receivedTerminalEvent = true;\n } else if (event.type === 'error') {\n terminalError = event.message;\n receivedTerminalEvent = true;\n }\n }\n } finally {\n setIsWorking(false);\n }\n if (terminalError !== undefined) {\n logger?.error(`AI streaming ended in error: ${terminalError}`);\n return fail(terminalError);\n }\n if (!receivedTerminalEvent) {\n const message = 'AI stream ended without a terminal done or error event';\n logger?.error(message);\n return fail(message);\n }\n return succeed({ fullText, truncated });\n },\n [settings, keyStore, logger]\n );\n\n return {\n actions,\n isWorking,\n copyPrompt,\n generateDirect,\n generateImages,\n listModels,\n streamDirect\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/packlets/badge/index.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.renderStatusBadge = void 0;
|
|
25
|
+
var renderStatusBadge_1 = require("./renderStatusBadge");
|
|
26
|
+
Object.defineProperty(exports, "renderStatusBadge", { enumerable: true, get: function () { return renderStatusBadge_1.renderStatusBadge; } });
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/badge/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nexport { renderStatusBadge } from './renderStatusBadge';\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type BadgeTone = 'info' | 'warning' | 'danger';
|
|
3
|
+
interface IBadgeLike {
|
|
4
|
+
readonly kind: 'dot' | 'count';
|
|
5
|
+
readonly tone?: BadgeTone;
|
|
6
|
+
readonly count?: number;
|
|
7
|
+
readonly ariaLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function renderStatusBadge(badge: IBadgeLike): React.ReactElement;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=renderStatusBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/packlets/badge/renderStatusBadge.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAmBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,YAAY,CAevE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
24
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.renderStatusBadge = renderStatusBadge;
|
|
28
|
+
const react_1 = __importDefault(require("react"));
|
|
29
|
+
const BADGE_COUNT_BASE_CLASSES = 'ml-1.5 min-w-4 px-1.5 py-0.5 text-[0.6875rem] inline-flex shrink-0 items-center justify-center rounded-full font-medium leading-none';
|
|
30
|
+
const BADGE_DOT_BASE_CLASSES = 'ml-1.5 h-2 w-2 inline-block shrink-0 rounded-full';
|
|
31
|
+
const BADGE_COUNT_TONE_CLASSES = {
|
|
32
|
+
info: 'bg-status-info-bg text-status-info-text',
|
|
33
|
+
warning: 'bg-status-warning-bg text-status-warning-text',
|
|
34
|
+
danger: 'bg-status-error-bg text-status-error-text'
|
|
35
|
+
};
|
|
36
|
+
const BADGE_DOT_TONE_CLASSES = {
|
|
37
|
+
info: 'bg-status-info-icon',
|
|
38
|
+
warning: 'bg-status-warning-icon',
|
|
39
|
+
danger: 'bg-status-error-icon'
|
|
40
|
+
};
|
|
41
|
+
function renderStatusBadge(badge) {
|
|
42
|
+
var _a, _b;
|
|
43
|
+
const tone = (_a = badge.tone) !== null && _a !== void 0 ? _a : 'info';
|
|
44
|
+
const ariaLabel = badge.ariaLabel;
|
|
45
|
+
if (badge.kind === 'dot') {
|
|
46
|
+
return (react_1.default.createElement("span", { className: `${BADGE_DOT_BASE_CLASSES} ${BADGE_DOT_TONE_CLASSES[tone]}`, "aria-label": ariaLabel }));
|
|
47
|
+
}
|
|
48
|
+
return (react_1.default.createElement("span", { className: `${BADGE_COUNT_BASE_CLASSES} ${BADGE_COUNT_TONE_CLASSES[tone]}`, "aria-label": ariaLabel }, (_b = badge.count) !== null && _b !== void 0 ? _b : 0));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=renderStatusBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderStatusBadge.js","sourceRoot":"","sources":["../../../src/packlets/badge/renderStatusBadge.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;AA8BH,8CAeC;AA3CD,kDAA0B;AAW1B,MAAM,wBAAwB,GAC5B,sIAAsI,CAAC;AAEzI,MAAM,sBAAsB,GAAW,mDAAmD,CAAC;AAE3F,MAAM,wBAAwB,GAA8B;IAC1D,IAAI,EAAE,yCAAyC;IAC/C,OAAO,EAAE,+CAA+C;IACxD,MAAM,EAAE,2CAA2C;CACpD,CAAC;AAEF,MAAM,sBAAsB,GAA8B;IACxD,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,wBAAwB;IACjC,MAAM,EAAE,sBAAsB;CAC/B,CAAC;AAEF,SAAgB,iBAAiB,CAAC,KAAiB;;IACjD,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,mCAAI,MAAM,CAAC;IAClC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAElC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,CACL,wCAAM,SAAS,EAAE,GAAG,sBAAsB,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,gBAAc,SAAS,GAAI,CACxG,CAAC;IACJ,CAAC;IAED,OAAO,CACL,wCAAM,SAAS,EAAE,GAAG,wBAAwB,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE,gBAAc,SAAS,IACpG,MAAA,KAAK,CAAC,KAAK,mCAAI,CAAC,CACZ,CACR,CAAC;AACJ,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport React from 'react';\n\ntype BadgeTone = 'info' | 'warning' | 'danger';\n\ninterface IBadgeLike {\n readonly kind: 'dot' | 'count';\n readonly tone?: BadgeTone;\n readonly count?: number;\n readonly ariaLabel?: string;\n}\n\nconst BADGE_COUNT_BASE_CLASSES: string =\n 'ml-1.5 min-w-4 px-1.5 py-0.5 text-[0.6875rem] inline-flex shrink-0 items-center justify-center rounded-full font-medium leading-none';\n\nconst BADGE_DOT_BASE_CLASSES: string = 'ml-1.5 h-2 w-2 inline-block shrink-0 rounded-full';\n\nconst BADGE_COUNT_TONE_CLASSES: Record<BadgeTone, string> = {\n info: 'bg-status-info-bg text-status-info-text',\n warning: 'bg-status-warning-bg text-status-warning-text',\n danger: 'bg-status-error-bg text-status-error-text'\n};\n\nconst BADGE_DOT_TONE_CLASSES: Record<BadgeTone, string> = {\n info: 'bg-status-info-icon',\n warning: 'bg-status-warning-icon',\n danger: 'bg-status-error-icon'\n};\n\nexport function renderStatusBadge(badge: IBadgeLike): React.ReactElement {\n const tone = badge.tone ?? 'info';\n const ariaLabel = badge.ariaLabel;\n\n if (badge.kind === 'dot') {\n return (\n <span className={`${BADGE_DOT_BASE_CLASSES} ${BADGE_DOT_TONE_CLASSES[tone]}`} aria-label={ariaLabel} />\n );\n }\n\n return (\n <span className={`${BADGE_COUNT_BASE_CLASSES} ${BADGE_COUNT_TONE_CLASSES[tone]}`} aria-label={ariaLabel}>\n {badge.count ?? 0}\n </span>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CascadeContainer.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/CascadeContainer.tsx"],"names":[],"mappings":"AAsBA;;;GAGG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAQ9D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;CACnC;AAMD;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAChD,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,2DAA2D;IAC3D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CA2FzF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CascadeContainer.js","sourceRoot":"","sources":["../../../src/packlets/cascade/CascadeContainer.tsx"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Column cascade container — horizontal scroll of detail columns.\n * @packageDocumentation\n */\n\nimport React, { useCallback, useEffect, useRef } from 'react';\nimport { useResponsive } from '../responsive';\nimport { MobileCascadeStack } from './MobileCascadeStack';\n\n// ============================================================================\n// Cascade Column\n// ============================================================================\n\n/**\n * Describes a single column in the cascade.\n * @public\n */\nexport interface ICascadeColumn {\n /** Unique key for React reconciliation */\n readonly key: string;\n /** Breadcrumb label for this column */\n readonly label: string;\n /** Column content */\n readonly content: React.ReactNode;\n}\n\n// ============================================================================\n// CascadeContainer Props\n// ============================================================================\n\n/**\n * Props for the CascadeContainer component.\n * @public\n */\nexport interface ICascadeContainerProps {\n /** Ordered columns (left-to-right) */\n readonly columns: ReadonlyArray<ICascadeColumn>;\n /** Callback to pop the cascade back to a specific depth (0 = clear all) */\n readonly onPopTo: (depth: number) => void;\n /** Minimum column width in CSS units (default: '400px') */\n readonly minColumnWidth?: string;\n /** Callback when the user clicks inside a cascade column (signals list should collapse) */\n readonly onFocus?: () => void;\n /** Label for the breadcrumb root link (default: 'List') */\n readonly rootLabel?: string;\n}\n\n// ============================================================================\n// CascadeContainer Component\n// ============================================================================\n\n/**\n * Horizontal scroll container for the column cascade.\n *\n * Renders a breadcrumb trail at the top and horizontally-scrollable\n * detail columns below. Auto-scrolls to the rightmost column when\n * a new column is pushed.\n *\n * @public\n */\nexport function CascadeContainer(props: ICascadeContainerProps): React.ReactElement | null {\n const { columns, onPopTo, minColumnWidth = '400px', onFocus, rootLabel = 'List' } = props;\n const { layoutMode } = useResponsive();\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const handleMouseDown = useCallback((): void => {\n onFocus?.();\n }, [onFocus]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent): void => {\n if (e.key === 'Escape' || e.key === 'ArrowLeft') {\n e.preventDefault();\n e.stopPropagation();\n if (columns.length > 1) {\n onPopTo(columns.length - 1);\n } else {\n onPopTo(0);\n }\n }\n },\n [columns.length, onPopTo]\n );\n\n // Auto-scroll to rightmost column when columns change\n useEffect(() => {\n if (scrollRef.current) {\n scrollRef.current.scrollTo({\n left: scrollRef.current.scrollWidth,\n behavior: 'smooth'\n });\n }\n }, [columns.length]);\n\n if (layoutMode === 'mobile') {\n return <MobileCascadeStack {...props} />;\n }\n\n if (columns.length === 0) {\n return null;\n }\n\n return (\n <div\n className=\"flex flex-col flex-1 overflow-hidden outline-none\"\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n {/* Breadcrumb trail */}\n <div className=\"flex items-center gap-1 px-3 py-1.5 bg-surface-alt border-b border-border text-xs shrink-0 overflow-x-auto\">\n <button\n onClick={(): void => onPopTo(0)}\n className=\"text-brand-accent hover:text-brand-primary hover:underline shrink-0\"\n >\n {rootLabel}\n </button>\n {columns.map((col, idx) => (\n <React.Fragment key={col.key}>\n <span className=\"text-muted shrink-0\">/</span>\n {idx < columns.length - 1 ? (\n <button\n onClick={(): void => onPopTo(idx + 1)}\n className=\"text-brand-accent hover:text-brand-primary hover:underline truncate max-w-[200px] shrink-0\"\n >\n {col.label}\n </button>\n ) : (\n <span className=\"text-secondary font-medium truncate max-w-[200px] shrink-0\">{col.label}</span>\n )}\n </React.Fragment>\n ))}\n </div>\n\n {/* Columns */}\n <div\n ref={scrollRef}\n className=\"flex flex-1 overflow-x-auto overflow-y-hidden\"\n onMouseDown={handleMouseDown}\n >\n {columns.map((col) => (\n <div\n key={col.key}\n className=\"flex flex-col shrink-0 border-r border-border overflow-y-auto\"\n style={{ minWidth: minColumnWidth, width: minColumnWidth }}\n >\n {col.content}\n </div>\n ))}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComparisonView.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/ComparisonView.tsx"],"names":[],"mappings":"AAsBA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;CACpD;AAMD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CA4B9E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComparisonView.js","sourceRoot":"","sources":["../../../src/packlets/cascade/ComparisonView.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;AA+CH,wCA4BC;AAzED;;;GAGG;AAEH,kDAA0B;AA4B1B,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E;;;;;;;GAOG;AACH,SAAgB,cAAc,CAAC,KAA2B;IACxD,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAE1B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CACL,uCAAK,SAAS,EAAC,4DAA4D,0CAErE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,uCAAK,SAAS,EAAC,6BAA6B,IACzC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,uCACE,GAAG,EAAE,GAAG,CAAC,GAAG,EACZ,SAAS,EAAC,qFAAqF;QAG/F,uCAAK,SAAS,EAAC,4DAA4D;YACzE,wCAAM,SAAS,EAAC,mDAAmD,IAAE,GAAG,CAAC,KAAK,CAAQ,CAClF;QAEN,uCAAK,SAAS,EAAC,wBAAwB,IAAE,GAAG,CAAC,OAAO,CAAO,CACvD,CACP,CAAC,CACE,CACP,CAAC;AACJ,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * ComparisonView — side-by-side read-only comparison of 2–4 entities.\n * @packageDocumentation\n */\n\nimport React from 'react';\n\n// ============================================================================\n// ComparisonView Props\n// ============================================================================\n\n/**\n * A single column in the comparison view.\n * @public\n */\nexport interface IComparisonColumn {\n /** Unique key for React reconciliation */\n readonly key: string;\n /** Column header label */\n readonly label: string;\n /** Column content (typically a detail component) */\n readonly content: React.ReactNode;\n}\n\n/**\n * Props for the ComparisonView component.\n * @public\n */\nexport interface IComparisonViewProps {\n /** Columns to compare (2–4) */\n readonly columns: ReadonlyArray<IComparisonColumn>;\n}\n\n// ============================================================================\n// ComparisonView Component\n// ============================================================================\n\n/**\n * Side-by-side comparison view for entities.\n *\n * Renders 2–4 entity detail views in equal-width columns with\n * synchronized scrolling (future) and column headers.\n *\n * @public\n */\nexport function ComparisonView(props: IComparisonViewProps): React.ReactElement {\n const { columns } = props;\n\n if (columns.length < 2) {\n return (\n <div className=\"flex flex-1 items-center justify-center text-muted text-sm\">\n Select at least 2 items to compare.\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-1 overflow-hidden\">\n {columns.map((col) => (\n <div\n key={col.key}\n className=\"flex flex-col flex-1 min-w-0 border-r border-border last:border-r-0 overflow-hidden\"\n >\n {/* Column header */}\n <div className=\"px-3 py-1.5 bg-surface-alt border-b border-border shrink-0\">\n <span className=\"text-xs font-medium text-secondary truncate block\">{col.label}</span>\n </div>\n {/* Column content */}\n <div className=\"flex-1 overflow-y-auto\">{col.content}</div>\n </div>\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityTabLayout.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/EntityTabLayout.tsx"],"names":[],"mappings":"AAsBA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAM1E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACvD,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC;IACpC,qCAAqC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC9D,kFAAkF;IAClF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,oEAAoE;IACpE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,sFAAsF;IACtF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACpE,iDAAiD;IACjD,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9C;AAMD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CA2HhF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityTabLayout.js","sourceRoot":"","sources":["../../../src/packlets/cascade/EntityTabLayout.tsx"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * EntityTabLayout — shared layout for entity tab content with list + cascade + collapse-on-focus.\n * @packageDocumentation\n */\n\nimport React from 'react';\n\nimport { CascadeContainer, type ICascadeColumn } from './CascadeContainer';\nimport { useResponsive } from '../responsive';\nimport { ComparisonView, type IComparisonColumn } from './ComparisonView';\n\n// ============================================================================\n// EntityTabLayout Props\n// ============================================================================\n\n/**\n * Props for the EntityTabLayout component.\n * @public\n */\nexport interface IEntityTabLayoutProps {\n /** The entity list content (rendered in the collapsible left panel) */\n readonly list: React.ReactNode;\n /** Cascade columns to display (empty array = no cascade) */\n readonly cascadeColumns: ReadonlyArray<ICascadeColumn>;\n /** Callback to pop the cascade back to a specific depth (0 = clear all) */\n readonly onPopTo: (depth: number) => void;\n /** Whether the entity list is currently collapsed */\n readonly listCollapsed: boolean;\n /** Callback to collapse the entity list (fired when user clicks inside cascade) */\n readonly onListCollapse: () => void;\n /** Whether compare mode is active */\n readonly compareMode?: boolean;\n /** Columns for the comparison view (when compare mode is active with 2+ selections) */\n readonly comparisonColumns?: ReadonlyArray<IComparisonColumn>;\n /** Whether the comparison view is actively showing (user explicitly triggered) */\n readonly showingComparison?: boolean;\n /** Callback to exit the comparison view (back to selection list) */\n readonly onExitComparison?: () => void;\n /** Columns for variation comparison (when comparing variations of a single recipe) */\n readonly variationCompareColumns?: ReadonlyArray<IComparisonColumn>;\n /** Callback to exit variation comparison mode */\n readonly onExitVariationCompare?: () => void;\n}\n\n// ============================================================================\n// EntityTabLayout Component\n// ============================================================================\n\n/**\n * Shared layout for entity tab content.\n *\n * Renders an entity list on the left and a cascade container on the right.\n * The list stays expanded while browsing (selecting items in the list).\n * It collapses when the user clicks inside the cascade detail pane,\n * signaling they are focused on the detail rather than browsing.\n *\n * @public\n */\nexport function EntityTabLayout(props: IEntityTabLayoutProps): React.ReactElement {\n const { list, cascadeColumns, onPopTo, listCollapsed, onListCollapse, compareMode, comparisonColumns } =\n props;\n\n const variationCompareColumns = props.variationCompareColumns;\n const onExitVariationCompare = props.onExitVariationCompare;\n const showingComparison = props.showingComparison ?? false;\n const onExitComparison = props.onExitComparison;\n const { layoutMode } = useResponsive();\n\n const isVariationCompare = variationCompareColumns !== undefined && variationCompareColumns.length >= 2;\n const showComparison =\n !isVariationCompare &&\n showingComparison &&\n comparisonColumns !== undefined &&\n comparisonColumns.length >= 2;\n const showCascade = !compareMode && !isVariationCompare && !showComparison && cascadeColumns.length > 0;\n const hasCascadeOrCompare = cascadeColumns.length > 0 || showComparison;\n\n // Variation compare banner — shared between mobile and desktop\n const variationCompareBanner = isVariationCompare && onExitVariationCompare && (\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-status-warning-bg border-b border-status-warning-border shrink-0\">\n <span className=\"text-xs text-status-warning-text\">\n Comparing {variationCompareColumns.length} variations\n </span>\n <button\n onClick={onExitVariationCompare}\n className=\"px-2 py-0.5 text-xs rounded border border-status-warning-border text-status-warning-text hover:bg-status-warning-surface transition-colors\"\n >\n Exit\n </button>\n </div>\n );\n\n // Mobile: show one pane at a time — list or cascade/comparison full-screen\n if (layoutMode === 'mobile') {\n const hasCascadeContent = showCascade || showComparison || isVariationCompare;\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n {variationCompareBanner}\n {hasCascadeContent ? (\n <>\n {showCascade && <CascadeContainer columns={cascadeColumns} onPopTo={onPopTo} />}\n {showComparison && (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-status-info-bg border-b border-status-info-border shrink-0\">\n <span className=\"text-xs text-status-info-text\">\n Comparing {comparisonColumns.length} items\n </span>\n {onExitComparison && (\n <button\n onClick={onExitComparison}\n className=\"px-2 py-0.5 text-xs rounded border border-status-info-border text-status-info-text hover:bg-status-info-surface transition-colors\"\n >\n ← Back to list\n </button>\n )}\n </div>\n <ComparisonView columns={comparisonColumns} />\n </div>\n )}\n {isVariationCompare && <ComparisonView columns={variationCompareColumns} />}\n </>\n ) : (\n <div className=\"flex flex-col flex-1 overflow-hidden\">{list}</div>\n )}\n </div>\n );\n }\n\n // Desktop/compact: side-by-side list and cascade\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n {variationCompareBanner}\n\n {/* Main content area */}\n <div className=\"flex flex-1 overflow-hidden\">\n {/* Entity list — proportional width when cascade is showing, full width when browsing */}\n <div\n className={`flex flex-col overflow-hidden transition-all ${\n isVariationCompare || showComparison\n ? 'w-0 min-w-0'\n : listCollapsed\n ? 'w-0 min-w-0'\n : hasCascadeOrCompare\n ? 'w-1/4 max-w-xs shrink-0 border-r border-border'\n : 'w-full max-w-sm shrink-0 border-r border-border'\n }`}\n >\n {list}\n </div>\n\n {/* Cascade columns (normal mode) */}\n {showCascade && (\n <CascadeContainer columns={cascadeColumns} onPopTo={onPopTo} onFocus={onListCollapse} />\n )}\n\n {/* Entity comparison view (compare mode — explicitly triggered) */}\n {showComparison && (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-status-info-bg border-b border-status-info-border shrink-0\">\n <span className=\"text-xs text-status-info-text\">\n Comparing {comparisonColumns.length} items\n </span>\n {onExitComparison && (\n <button\n onClick={onExitComparison}\n className=\"px-2 py-0.5 text-xs rounded border border-status-info-border text-status-info-text hover:bg-status-info-surface transition-colors\"\n >\n ← Back to list\n </button>\n )}\n </div>\n <ComparisonView columns={comparisonColumns} />\n </div>\n )}\n\n {/* Variation comparison view */}\n {isVariationCompare && <ComparisonView columns={variationCompareColumns} />}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileCascadeStack.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/MobileCascadeStack.tsx"],"names":[],"mappings":"AAsBA;;;GAGG;AAEH,OAAO,KAAsB,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CA+C3F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileCascadeStack.js","sourceRoot":"","sources":["../../../src/packlets/cascade/MobileCascadeStack.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBH,gDA+CC;AAnED;;;GAGG;AAEH,+CAA2C;AAG3C;;;;;;;;;;;GAWG;AACH,SAAgB,kBAAkB,CAAC,KAA6B;IAC9D,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,KAAK,CAAC;IAEvD,MAAM,UAAU,GAAG,IAAA,mBAAW,EAAC,GAAS,EAAE;QACxC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAE9B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAErF,OAAO,CACL,uCAAK,SAAS,EAAC,sCAAsC;QAEnD,uCAAK,SAAS,EAAC,kFAAkF;YAC/F,0CACE,OAAO,EAAE,UAAU,EACnB,SAAS,EAAC,4EAA4E,gBAC1E,WAAW,SAAS,EAAE;gBAElC,uCACE,SAAS,EAAC,kBAAkB,EAC5B,IAAI,EAAC,MAAM,EACX,OAAO,EAAC,WAAW,EACnB,WAAW,EAAE,CAAC,EACd,MAAM,EAAC,cAAc;oBAErB,wCAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,CAAC,EAAC,6BAA6B,GAAG,CACjF;gBACL,SAAS,CACH;YACR,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CACrB,wCAAM,SAAS,EAAC,qCAAqC,IAAE,aAAa,CAAC,KAAK,CAAQ,CACnF,CACG;QAGN,uCAAK,SAAS,EAAC,sCAAsC,IAAE,aAAa,CAAC,OAAO,CAAO,CAC/E,CACP,CAAC;AACJ,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Mobile view-stack cascade — one full-screen column at a time with back navigation.\n * @packageDocumentation\n */\n\nimport React, { useCallback } from 'react';\nimport { type ICascadeContainerProps } from './CascadeContainer';\n\n/**\n * Mobile replacement for {@link CascadeContainer}.\n *\n * Shows the rightmost (deepest) cascade column full-screen with a back button\n * that pops one level at a time. At the first column, back returns to the list\n * by calling `onPopTo(0)`.\n *\n * Accepts the same props as {@link CascadeContainer} so `CascadeContainer` can\n * delegate to it transparently on mobile.\n *\n * @public\n */\nexport function MobileCascadeStack(props: ICascadeContainerProps): React.ReactElement | null {\n const { columns, onPopTo, rootLabel = 'List' } = props;\n\n const handleBack = useCallback((): void => {\n if (columns.length > 1) {\n onPopTo(columns.length - 1);\n } else {\n onPopTo(0);\n }\n }, [columns.length, onPopTo]);\n\n if (columns.length === 0) {\n return null;\n }\n\n const currentColumn = columns[columns.length - 1];\n const backLabel = columns.length > 1 ? columns[columns.length - 2].label : rootLabel;\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n {/* Back navigation header */}\n <div className=\"flex items-center gap-2 px-3 py-2 bg-surface-alt border-b border-border shrink-0\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-1 text-sm text-brand-accent hover:text-brand-primary\"\n aria-label={`Back to ${backLabel}`}\n >\n <svg\n className=\"w-4 h-4 shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={2}\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15.75 19.5L8.25 12l7.5-7.5\" />\n </svg>\n {backLabel}\n </button>\n {columns.length > 1 && (\n <span className=\"ml-auto text-xs text-muted truncate\">{currentColumn.label}</span>\n )}\n </div>\n\n {/* Current column — full screen, scrollable */}\n <div className=\"flex flex-col flex-1 overflow-y-auto\">{currentColumn.content}</div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/index.ts"],"names":[],"mappings":"AAsBA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,KAAK,oBAAoB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrG,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG3E,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,aAAa,EACd,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/cascade/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH;;;GAGG;AAEH,gBAAgB;AAChB,uDAAwG;AAA/F,oHAAA,gBAAgB,OAAA;AACzB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AACxB,mDAAqG;AAA5F,gHAAA,cAAc,OAAA;AAEvB,sBAAsB;AACtB,iCAKiB;AADf,8GAAA,qBAAqB,OAAA;AAGvB,2BAA2B;AAC3B,iEAA2E;AAAlE,oHAAA,WAAW,OAAA;AAAE,4HAAA,mBAAmB,OAAA;AAEzC,8BAA8B;AAC9B,iDAMyB;AADvB,8GAAA,aAAa,OAAA","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Cascade packlet - column cascade container, layout, and semantic operations.\n * @packageDocumentation\n */\n\n// UI components\nexport { CascadeContainer, type ICascadeContainerProps, type ICascadeColumn } from './CascadeContainer';\nexport { MobileCascadeStack } from './MobileCascadeStack';\nexport { EntityTabLayout, type IEntityTabLayoutProps } from './EntityTabLayout';\nexport { ComparisonView, type IComparisonViewProps, type IComparisonColumn } from './ComparisonView';\n\n// Cascade model types\nexport {\n type CascadeColumnMode,\n type CascadeEntryOrigin,\n type ICascadeEntryBase,\n CASCADE_NEW_ENTITY_ID\n} from './model';\n\n// Cascade transition hooks\nexport { useSquashAt, useCascadeDrillDown } from './useCascadeTransitions';\n\n// Semantic cascade operations\nexport {\n type ICascadeConflict,\n type ICascadeFind,\n type CascadeEntrySpec,\n type ICascadeOps,\n useCascadeOps\n} from './useCascadeOps';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/packlets/cascade/model.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;GASG;AAMH;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvE;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAMtD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAqB,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|