@cmodjs/core 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/ui.md +1 -1
- package/package.json +1 -1
- package/records.d.ts +1 -1
- package/records.js +1 -1
- package/runtime/lifecycle.js +1 -1
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +28 -5
- package/src/records.ts +1 -1
- package/src/runtime/lifecycle.ts +1 -1
- package/src/runtime/toaster.ts +27 -5
package/docs/ui.md
CHANGED
|
@@ -411,7 +411,7 @@ export const indexer = defineMod({
|
|
|
411
411
|
|
|
412
412
|
An error cmod catches for a mod, such as a hook that throws, shows as an error toast that stays, with its buttons on a line under the message:
|
|
413
413
|
|
|
414
|
-
- `Retry` runs the failed step again, where running it again can work: an install, the mod's setup, or a program that stopped.
|
|
414
|
+
- `Retry` runs the failed step again, where running it again can work: an install, the mod's setup, or a program that stopped. The toast stays where it is, and the step's progress shows in place of its description, or of its buttons when it has none, until the try ends.
|
|
415
415
|
- `Report` replaces the toast's buttons with the ways to tell the mod's author, with the keys on the first, and `Back` returns to the buttons. The same failure happening again keeps them open. `Ask Claude to report it` sends this session a prompt to file the report with the person's own tools, and Claude Code asks before each step. `Report it on GitHub (opens your browser)` opens a filled-in issue on the mod's `supportUrl` or `repository` in its `plugin.json`, or `Email the author (opens your mail app)` writes to its `author.email`. `Copy the error` copies the mod's name and version, the `@cmodjs/core` version, the message, the whole cause, and the stack.
|
|
416
416
|
- `Hide` closes the toast and keeps the same failure hidden in later sessions. A new version of the mod, or a different failure, shows again.
|
|
417
417
|
|
package/package.json
CHANGED
package/records.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export declare function marketplaceOf(root: string, name: string): string | unde
|
|
|
72
72
|
export declare function updatesToTurnOn(marketplace: string | undefined, settings: unknown, known: unknown): string | undefined;
|
|
73
73
|
export declare function consentPath(store: string): string;
|
|
74
74
|
export declare function parseConsent(value: unknown, path: string): Record<string, string[]>;
|
|
75
|
-
export declare const coreVersion = "1.5.
|
|
75
|
+
export declare const coreVersion = "1.5.1";
|
|
76
76
|
export declare function oldestCmodFor(steps: Steps): string;
|
|
77
77
|
export declare function keyWords(keys: Readonly<Record<string, string>> | undefined): string;
|
|
78
78
|
export declare function scriptPaths(command: string): string[];
|
package/records.js
CHANGED
|
@@ -220,7 +220,7 @@ function readKeys(keys) {
|
|
|
220
220
|
}
|
|
221
221
|
return { keys: commands };
|
|
222
222
|
}
|
|
223
|
-
export const coreVersion = '1.5.
|
|
223
|
+
export const coreVersion = '1.5.1';
|
|
224
224
|
export function oldestCmodFor(steps) {
|
|
225
225
|
if (steps.permissions?.some((item) => item === 'tasks' || item.startsWith('config:')) === true)
|
|
226
226
|
return '1.0.0';
|
package/runtime/lifecycle.js
CHANGED
|
@@ -301,7 +301,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
301
301
|
claude().ui.log(`${name} waits for consent to run ${event.install}. Run cmod install ${name} in a terminal.`);
|
|
302
302
|
return;
|
|
303
303
|
}
|
|
304
|
-
|
|
304
|
+
runtime?.toaster.toast.info('Installing', { id: installId, description: 'Waiting for your answer', duration: Infinity });
|
|
305
305
|
const updates = await updatesToShow().catch((error) => {
|
|
306
306
|
toastFailure({ id: 'cmod:updates', message: 'Could not read whether its marketplace updates automatically', error });
|
|
307
307
|
return undefined;
|
package/runtime/toaster.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;
|
|
1
|
+
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;AAuCD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CA6PtG"}
|
package/runtime/toaster.js
CHANGED
|
@@ -32,7 +32,7 @@ export function createToaster(claude, name, version) {
|
|
|
32
32
|
let showsAll = false;
|
|
33
33
|
let bandId;
|
|
34
34
|
const changed = () => {
|
|
35
|
-
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading');
|
|
35
|
+
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading' || entry.trying !== undefined);
|
|
36
36
|
if (isLoading && spinning === undefined) {
|
|
37
37
|
spinning = claude.clock.every(spinnerMs, () => {
|
|
38
38
|
frame += 1;
|
|
@@ -75,6 +75,11 @@ export function createToaster(claude, name, version) {
|
|
|
75
75
|
const put = (type, message, options, actions, hides) => {
|
|
76
76
|
const id = options.id ?? (lastId += 1);
|
|
77
77
|
const previous = entries.get(id);
|
|
78
|
+
if (previous?.trying !== undefined && type === 'loading') {
|
|
79
|
+
previous.trying = options.description ?? message;
|
|
80
|
+
changed();
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
78
83
|
if (previous !== undefined)
|
|
79
84
|
disarm(previous);
|
|
80
85
|
const reporting = hides !== undefined && previous?.hides === hides ? previous.reporting : undefined;
|
|
@@ -149,7 +154,23 @@ export function createToaster(claude, name, version) {
|
|
|
149
154
|
if ('deny' in focused)
|
|
150
155
|
claude.ui.log(`${name}: the Report choices did not take the keys: ${String(focused.deny)}`, { to: 'debug' });
|
|
151
156
|
};
|
|
152
|
-
const
|
|
157
|
+
const tryAgain = async () => {
|
|
158
|
+
const shown = entries.get(id);
|
|
159
|
+
if (shown === undefined || retry === undefined || shown.trying !== undefined)
|
|
160
|
+
return;
|
|
161
|
+
shown.trying = 'Starting';
|
|
162
|
+
changed();
|
|
163
|
+
try {
|
|
164
|
+
await retry();
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
if (entries.get(id) === shown) {
|
|
168
|
+
shown.trying = undefined;
|
|
169
|
+
changed();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const actions = [...(retry === undefined ? [] : [{ label: 'Retry', onClick: tryAgain }]), { label: 'Report', onClick: startReporting }];
|
|
153
174
|
return put('error', message, { id, ...(description === undefined ? {} : { description }), duration: Infinity }, actions, hides);
|
|
154
175
|
};
|
|
155
176
|
const toggle = (entry) => () => {
|
|
@@ -160,14 +181,16 @@ export function createToaster(claude, name, version) {
|
|
|
160
181
|
const row = (entry) => {
|
|
161
182
|
const { description } = entry.options;
|
|
162
183
|
const isText = typeof description === 'string';
|
|
163
|
-
const icon = entry.type === 'loading' ? { glyph: spinner[frame % spinner.length], color: 'claude' } : icons[entry.type];
|
|
184
|
+
const icon = entry.type === 'loading' || entry.trying !== undefined ? { glyph: spinner[frame % spinner.length], color: 'claude' } : icons[entry.type];
|
|
164
185
|
const message = isText ? Button({ plain: true, key: `toast:${entry.id}:open`, label: entry.message, onPress: toggle(entry) }) : Text({ children: entry.message });
|
|
165
186
|
const details = !isText ? description : Text({ dimColor: true, wrap: entry.isOpen ? 'wrap' : 'truncate-end', children: entry.isOpen ? description : firstLine(description) });
|
|
166
187
|
const fixed = (child) => Box({ flexShrink: 0, children: child });
|
|
167
188
|
const close = Button({ plain: true, dimColor: true, key: `toast:${entry.id}:close`, label: entry.hides === undefined ? '✕' : 'Hide', onPress: () => hide(entry) });
|
|
168
189
|
const actions = entry.actions.map((action, index) => Button({ plain: true, key: `toast:${entry.id}:${index === 0 ? 'action' : `action-${index}`}`, label: action.label, onPress: press(entry, action.onClick) }));
|
|
169
|
-
const
|
|
170
|
-
const
|
|
190
|
+
const buttons = Box({ gap: 2, flexWrap: 'wrap', children: [...actions, close] });
|
|
191
|
+
const trying = entry.trying === undefined ? undefined : Box({ gap: 1, children: [Text({ dimColor: true, children: 'Trying again:' }), typeof entry.trying === 'string' ? Text({ dimColor: true, wrap: 'truncate-end', children: entry.trying }) : entry.trying] });
|
|
192
|
+
const text = details === undefined ? [message] : [message, trying ?? details];
|
|
193
|
+
const under = trying !== undefined && details === undefined ? [trying] : entry.reporting === undefined ? [buttons] : reportChoices(entry, entry.reporting);
|
|
171
194
|
return Box({
|
|
172
195
|
gap: 2,
|
|
173
196
|
children: [
|
package/src/records.ts
CHANGED
|
@@ -250,7 +250,7 @@ function readKeys(keys: unknown): Steps {
|
|
|
250
250
|
return { keys: commands }
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
export const coreVersion = '1.5.
|
|
253
|
+
export const coreVersion = '1.5.1'
|
|
254
254
|
|
|
255
255
|
export function oldestCmodFor(steps: Steps): string {
|
|
256
256
|
if (steps.permissions?.some((item) => item === 'tasks' || item.startsWith('config:')) === true) return '1.0.0'
|
package/src/runtime/lifecycle.ts
CHANGED
|
@@ -367,7 +367,7 @@ export function createLifecycle<State extends object, Declared extends Options =
|
|
|
367
367
|
claude().ui.log(`${name} waits for consent to run ${event.install}. Run cmod install ${name} in a terminal.`)
|
|
368
368
|
return
|
|
369
369
|
}
|
|
370
|
-
|
|
370
|
+
runtime?.toaster.toast.info('Installing', { id: installId, description: 'Waiting for your answer', duration: Infinity })
|
|
371
371
|
const updates = await updatesToShow().catch((error: unknown) => {
|
|
372
372
|
toastFailure({ id: 'cmod:updates', message: 'Could not read whether its marketplace updates automatically', error })
|
|
373
373
|
return undefined
|
package/src/runtime/toaster.ts
CHANGED
|
@@ -32,6 +32,7 @@ type Entry = {
|
|
|
32
32
|
readonly hides?: string
|
|
33
33
|
isOpen: boolean
|
|
34
34
|
reporting?: Reporting | undefined
|
|
35
|
+
trying?: string | RenderElement | undefined
|
|
35
36
|
timer: Timer | undefined
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -71,7 +72,7 @@ export function createToaster(claude: Claude, name: string, version: () => strin
|
|
|
71
72
|
let bandId: string | undefined
|
|
72
73
|
|
|
73
74
|
const changed = () => {
|
|
74
|
-
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading')
|
|
75
|
+
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading' || entry.trying !== undefined)
|
|
75
76
|
if (isLoading && spinning === undefined) {
|
|
76
77
|
spinning = claude.clock.every(spinnerMs, () => {
|
|
77
78
|
frame += 1
|
|
@@ -113,6 +114,11 @@ export function createToaster(claude: Claude, name: string, version: () => strin
|
|
|
113
114
|
const put = (type: ToastType, message: string, options: ToastOptions, actions: readonly Action[], hides?: string): ToastId => {
|
|
114
115
|
const id = options.id ?? (lastId += 1)
|
|
115
116
|
const previous = entries.get(id)
|
|
117
|
+
if (previous?.trying !== undefined && type === 'loading') {
|
|
118
|
+
previous.trying = options.description ?? message
|
|
119
|
+
changed()
|
|
120
|
+
return id
|
|
121
|
+
}
|
|
116
122
|
if (previous !== undefined) disarm(previous)
|
|
117
123
|
const reporting = hides !== undefined && previous?.hides === hides ? previous.reporting : undefined
|
|
118
124
|
const entry: Entry = { id, type, message, options, actions, ...(hides === undefined ? {} : { hides }), isOpen: previous?.isOpen ?? false, reporting, timer: undefined }
|
|
@@ -192,7 +198,21 @@ export function createToaster(claude: Claude, name: string, version: () => strin
|
|
|
192
198
|
const focused = await claude.ui.focus({ requestId: bandId, key: `toast:${id}:ask-claude` })
|
|
193
199
|
if ('deny' in focused) claude.ui.log(`${name}: the Report choices did not take the keys: ${String(focused.deny)}`, { to: 'debug' })
|
|
194
200
|
}
|
|
195
|
-
const
|
|
201
|
+
const tryAgain = async () => {
|
|
202
|
+
const shown = entries.get(id)
|
|
203
|
+
if (shown === undefined || retry === undefined || shown.trying !== undefined) return
|
|
204
|
+
shown.trying = 'Starting'
|
|
205
|
+
changed()
|
|
206
|
+
try {
|
|
207
|
+
await retry()
|
|
208
|
+
} finally {
|
|
209
|
+
if (entries.get(id) === shown) {
|
|
210
|
+
shown.trying = undefined
|
|
211
|
+
changed()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const actions = [...(retry === undefined ? [] : [{ label: 'Retry', onClick: tryAgain }]), { label: 'Report', onClick: startReporting }]
|
|
196
216
|
return put('error', message, { id, ...(description === undefined ? {} : { description }), duration: Infinity }, actions, hides)
|
|
197
217
|
}
|
|
198
218
|
|
|
@@ -205,14 +225,16 @@ export function createToaster(claude: Claude, name: string, version: () => strin
|
|
|
205
225
|
const row = (entry: Entry): RenderElement => {
|
|
206
226
|
const { description } = entry.options
|
|
207
227
|
const isText = typeof description === 'string'
|
|
208
|
-
const icon = entry.type === 'loading' ? { glyph: spinner[frame % spinner.length] as string, color: 'claude' } : icons[entry.type]
|
|
228
|
+
const icon = entry.type === 'loading' || entry.trying !== undefined ? { glyph: spinner[frame % spinner.length] as string, color: 'claude' } : icons[entry.type]
|
|
209
229
|
const message = isText ? Button({ plain: true, key: `toast:${entry.id}:open`, label: entry.message, onPress: toggle(entry) }) : Text({ children: entry.message })
|
|
210
230
|
const details = !isText ? description : Text({ dimColor: true, wrap: entry.isOpen ? 'wrap' : 'truncate-end', children: entry.isOpen ? description : firstLine(description) })
|
|
211
231
|
const fixed = (child: RenderElement) => Box({ flexShrink: 0, children: child })
|
|
212
232
|
const close = Button({ plain: true, dimColor: true, key: `toast:${entry.id}:close`, label: entry.hides === undefined ? '✕' : 'Hide', onPress: () => hide(entry) })
|
|
213
233
|
const actions = entry.actions.map((action, index) => Button({ plain: true, key: `toast:${entry.id}:${index === 0 ? 'action' : `action-${index}`}`, label: action.label, onPress: press(entry, action.onClick) }))
|
|
214
|
-
const
|
|
215
|
-
const
|
|
234
|
+
const buttons = Box({ gap: 2, flexWrap: 'wrap', children: [...actions, close] })
|
|
235
|
+
const trying = entry.trying === undefined ? undefined : Box({ gap: 1, children: [Text({ dimColor: true, children: 'Trying again:' }), typeof entry.trying === 'string' ? Text({ dimColor: true, wrap: 'truncate-end', children: entry.trying }) : entry.trying] })
|
|
236
|
+
const text = details === undefined ? [message] : [message, trying ?? details]
|
|
237
|
+
const under = trying !== undefined && details === undefined ? [trying] : entry.reporting === undefined ? [buttons] : reportChoices(entry, entry.reporting)
|
|
216
238
|
return Box({
|
|
217
239
|
gap: 2,
|
|
218
240
|
children: [
|