@notis_ai/cli 0.2.0-beta.157.1 → 0.2.0-beta.159.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/dist/agent-hooks/notis-agent-hook.mjs +8296 -7912
- package/dist/base-skills/notis-apps/SKILL.md +34 -513
- package/dist/base-skills/notis-apps/references/architecture.md +164 -0
- package/dist/base-skills/notis-apps/references/design.md +165 -0
- package/dist/base-skills/notis-apps/references/release.md +99 -0
- package/dist/base-skills/notis-apps/references/sdk.md +61 -0
- package/dist/base-skills/notis-apps/references/troubleshooting.md +23 -0
- package/dist/base-skills/notis-cli/SKILL.md +19 -200
- package/dist/base-skills/notis-cli/references/app-delivery.md +18 -0
- package/dist/base-skills/notis-cli/references/native-databases.md +20 -0
- package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
- package/dist/base-skills/notis-cli/references/troubleshooting.md +39 -0
- package/dist/base-skills/notis-query/SKILL.md +13 -651
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
- package/dist/base-skills/notis-query/references/documents.md +50 -0
- package/dist/base-skills/notis-query/references/query.md +543 -0
- package/dist/skill-sync/index.js +24 -7
- package/dist/skill-sync/index.js.map +4 -4
- package/dist/skill-sync-worker.mjs +2989 -0
- package/package.json +1 -1
- package/src/cli.js +4 -0
- package/src/command-specs/diagnostics.js +37 -0
- package/src/command-specs/skills.js +23 -5
- package/src/runtime/profiles.js +5 -2
- package/src/runtime/skill-sync/cloud-client.ts +2 -1
- package/src/runtime/skill-sync/index.ts +24 -6
- package/src/runtime/skill-sync/types.ts +2 -0
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +50 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +36 -7
- package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +3 -1
- package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +138 -28
- package/template/packages/sdk/src/hooks/useDocuments.ts +4 -1
- package/template/packages/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +2 -8
- package/template/packages/sdk/src/index.ts +3 -0
- package/template/packages/sdk/src/interactions/actions.ts +14 -1
- package/template/packages/sdk/src/interactions/shortcuts.tsx +79 -19
- package/template/packages/sdk/src/interactions/visibility.ts +13 -0
- package/template/packages/sdk/src/interactions.ts +3 -0
- package/template/packages/sdk/src/queryCache.ts +10 -2
|
@@ -42,6 +42,8 @@ export interface UseShortcutsOptions {
|
|
|
42
42
|
sequenceTimeoutMs?: number;
|
|
43
43
|
/** Internal owner used to route collection shortcuts to the last interacted collection. */
|
|
44
44
|
collectionOwnerId?: string;
|
|
45
|
+
/** Evaluated at dispatch time, including for retained hidden app views. */
|
|
46
|
+
isAvailable?: () => boolean;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
interface ShortcutRegistration {
|
|
@@ -52,6 +54,8 @@ interface ShortcutRegistration {
|
|
|
52
54
|
enabled: boolean;
|
|
53
55
|
sequenceTimeoutMs: number;
|
|
54
56
|
collectionOwnerId?: string;
|
|
57
|
+
/** Evaluated at dispatch time, including for retained hidden app views. */
|
|
58
|
+
isAvailable?: () => boolean;
|
|
55
59
|
getShortcuts: () => ShortcutDefinition[];
|
|
56
60
|
}
|
|
57
61
|
|
|
@@ -92,6 +96,11 @@ export function createShortcutCollectionOwner(): string {
|
|
|
92
96
|
return `collection-${state.nextOwnerId++}`;
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
export function releaseShortcutCollection(ownerId: string): void {
|
|
100
|
+
const state = getShortcutCollectionState();
|
|
101
|
+
if (state.activeOwnerId === ownerId) state.activeOwnerId = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
export function activateShortcutCollection(ownerId: string): void {
|
|
96
105
|
getShortcutCollectionState().activeOwnerId = ownerId;
|
|
97
106
|
}
|
|
@@ -233,7 +242,7 @@ function applicableRegistrations(
|
|
|
233
242
|
registrations: Iterable<ShortcutRegistration>,
|
|
234
243
|
): ShortcutRegistration[] {
|
|
235
244
|
const ordered = Array.from(registrations)
|
|
236
|
-
.filter((registration) => registration.enabled)
|
|
245
|
+
.filter((registration) => registration.enabled && registration.isAvailable?.() !== false)
|
|
237
246
|
.sort((a, b) => b.priority - a.priority || b.order - a.order);
|
|
238
247
|
const activeCollectionOwner = getShortcutCollectionState().activeOwnerId;
|
|
239
248
|
const hasActiveCollection = Boolean(
|
|
@@ -248,6 +257,60 @@ function applicableRegistrations(
|
|
|
248
257
|
: ordered;
|
|
249
258
|
}
|
|
250
259
|
|
|
260
|
+
// Standalone app verification and legacy hosts may not install a provider.
|
|
261
|
+
// They must still arbitrate shortcuts together, not by effect/listener order.
|
|
262
|
+
const FALLBACK_REGISTRIES_SYMBOL = Symbol.for('notis.sdk.fallback_shortcut_registries');
|
|
263
|
+
interface FallbackShortcutRegistry {
|
|
264
|
+
registrations: Map<number, ShortcutRegistration>;
|
|
265
|
+
nextId: number;
|
|
266
|
+
dispatch: (event: KeyboardEvent) => void;
|
|
267
|
+
}
|
|
268
|
+
type FallbackShortcutGlobal = typeof globalThis & {
|
|
269
|
+
[FALLBACK_REGISTRIES_SYMBOL]?: WeakMap<Document, FallbackShortcutRegistry>;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
function registerFallbackShortcut(input: Omit<ShortcutRegistration, 'id' | 'order'>): () => void {
|
|
273
|
+
const scope = globalThis as FallbackShortcutGlobal;
|
|
274
|
+
const registries = scope[FALLBACK_REGISTRIES_SYMBOL] ??= new WeakMap();
|
|
275
|
+
const ownerDocument = document;
|
|
276
|
+
let registry = registries.get(ownerDocument);
|
|
277
|
+
if (!registry) {
|
|
278
|
+
const registrations = new Map<number, ShortcutRegistration>();
|
|
279
|
+
registry = {
|
|
280
|
+
registrations,
|
|
281
|
+
nextId: 1,
|
|
282
|
+
dispatch: (event) => {
|
|
283
|
+
if (event.defaultPrevented) return;
|
|
284
|
+
const editable = isEditableShortcutEvent(event);
|
|
285
|
+
const token = chordToken(eventChord(event));
|
|
286
|
+
for (const registration of applicableRegistrations(registrations.values())) {
|
|
287
|
+
for (const shortcut of registration.getShortcuts()) {
|
|
288
|
+
if (shortcut.enabled === false || (editable && !shortcut.allowInEditable) || (event.repeat && !shortcut.allowRepeat)) continue;
|
|
289
|
+
const match = parseShortcut(shortcut).some(
|
|
290
|
+
(candidate) => candidate.sequence.length === 1 && chordToken(candidate.sequence[0]!) === token,
|
|
291
|
+
);
|
|
292
|
+
if (match) {
|
|
293
|
+
consumeShortcut(event, shortcut);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
registries.set(ownerDocument, registry);
|
|
301
|
+
ownerDocument.addEventListener('keydown', registry.dispatch, true);
|
|
302
|
+
}
|
|
303
|
+
const id = registry.nextId++;
|
|
304
|
+
registry.registrations.set(id, { ...input, id, order: id });
|
|
305
|
+
return () => {
|
|
306
|
+
registry.registrations.delete(id);
|
|
307
|
+
if (registry.registrations.size === 0) {
|
|
308
|
+
ownerDocument.removeEventListener('keydown', registry.dispatch, true);
|
|
309
|
+
registries.delete(ownerDocument);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
251
314
|
function shortcutToken(raw: string): string | null {
|
|
252
315
|
const definition: ShortcutDefinition = { id: '', keys: raw, onTrigger: () => undefined };
|
|
253
316
|
const parsed = parseShortcut(definition)[0];
|
|
@@ -575,7 +638,10 @@ export function useShortcuts(
|
|
|
575
638
|
priority = 0,
|
|
576
639
|
sequenceTimeoutMs = 1500,
|
|
577
640
|
collectionOwnerId,
|
|
641
|
+
isAvailable,
|
|
578
642
|
} = options;
|
|
643
|
+
const availableRef = useRef(isAvailable);
|
|
644
|
+
availableRef.current = isAvailable;
|
|
579
645
|
|
|
580
646
|
useEffect(() => {
|
|
581
647
|
if (!registry || !enabled) return;
|
|
@@ -585,30 +651,24 @@ export function useShortcuts(
|
|
|
585
651
|
priority: SHORTCUT_SCOPE_PRIORITY[scope] + priority,
|
|
586
652
|
sequenceTimeoutMs,
|
|
587
653
|
collectionOwnerId,
|
|
588
|
-
|
|
654
|
+
isAvailable: () => availableRef.current?.() !== false,
|
|
655
|
+
getShortcuts: () => availableRef.current?.() === false ? [] : shortcutsRef.current,
|
|
589
656
|
});
|
|
590
657
|
}, [collectionOwnerId, enabled, priority, registry, scope, sequenceTimeoutMs]);
|
|
591
658
|
|
|
592
659
|
// Compatibility fallback for SDK components rendered outside a provider.
|
|
593
660
|
useEffect(() => {
|
|
594
661
|
if (registry || !enabled) return;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
document.addEventListener('keydown', handleKeyDown, true);
|
|
610
|
-
return () => document.removeEventListener('keydown', handleKeyDown, true);
|
|
611
|
-
}, [enabled, registry]);
|
|
662
|
+
return registerFallbackShortcut({
|
|
663
|
+
enabled,
|
|
664
|
+
scope,
|
|
665
|
+
priority: SHORTCUT_SCOPE_PRIORITY[scope] + priority,
|
|
666
|
+
sequenceTimeoutMs,
|
|
667
|
+
collectionOwnerId,
|
|
668
|
+
isAvailable: () => availableRef.current?.() !== false,
|
|
669
|
+
getShortcuts: () => shortcutsRef.current,
|
|
670
|
+
});
|
|
671
|
+
}, [collectionOwnerId, enabled, priority, registry, scope, sequenceTimeoutMs]);
|
|
612
672
|
}
|
|
613
673
|
|
|
614
674
|
export function shortcutDisplay(raw: string): string {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Visibility across light and Shadow DOM, without relying on layout (virtual rows may have no rect). */
|
|
2
|
+
export function isInteractionElementVisible(element: HTMLElement | null): boolean {
|
|
3
|
+
if (!element?.isConnected) return false;
|
|
4
|
+
let current: Element | null = element;
|
|
5
|
+
while (current) {
|
|
6
|
+
if (current.hasAttribute('hidden') || current.hasAttribute('inert') || current.getAttribute('aria-hidden') === 'true') return false;
|
|
7
|
+
const style = current.ownerDocument.defaultView?.getComputedStyle(current);
|
|
8
|
+
if (style?.display === 'none' || style?.visibility === 'hidden') return false;
|
|
9
|
+
const root: Node = current.getRootNode();
|
|
10
|
+
current = current.parentElement ?? ('host' in root ? (root as ShadowRoot).host : null);
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
@@ -40,3 +40,6 @@ export type { MultiSelectDragOverlayProps } from './components/MultiSelectDragOv
|
|
|
40
40
|
export type { MultiSelectCheckboxProps as SelectionCheckboxProps } from './components/MultiSelectCheckbox';
|
|
41
41
|
export type { MultiSelectDragOverlayProps as SelectionMarqueeProps } from './components/MultiSelectDragOverlay';
|
|
42
42
|
export type { ShortcutHint, ShortcutHintsProps } from './components/ShortcutHints';
|
|
43
|
+
|
|
44
|
+
export { useLongPressSelection } from './hooks/useLongPressSelection';
|
|
45
|
+
export { isInteractionElementVisible } from './interactions/visibility';
|
|
@@ -68,6 +68,8 @@ export function createQueryClient(options: {
|
|
|
68
68
|
maxEntries?: number;
|
|
69
69
|
now?: () => number;
|
|
70
70
|
schedule?: ReturnType<typeof createPrefetchQueue>;
|
|
71
|
+
/** Bounds idempotent reads only; never retries the underlying callback. */
|
|
72
|
+
readTimeoutMs?: number;
|
|
71
73
|
} = {}): NotisQueryClient {
|
|
72
74
|
const entries = new Map<string, Entry>();
|
|
73
75
|
const now = options.now ?? Date.now;
|
|
@@ -117,14 +119,20 @@ export function createQueryClient(options: {
|
|
|
117
119
|
const generation = ++value.generation;
|
|
118
120
|
const requestEpoch = epoch;
|
|
119
121
|
const canCommit = () => epoch === requestEpoch && value.generation === generation && entries.get(key) === value;
|
|
120
|
-
|
|
122
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
123
|
+
const deadline = new Promise<never>((_, reject) => {
|
|
124
|
+
timeout = setTimeout(() => reject(new Error('This read took too long. Please retry.')), options.readTimeoutMs ?? 30_000);
|
|
125
|
+
// Node-based verification must not stay alive for a retired browser read.
|
|
126
|
+
if (typeof timeout === 'object' && 'unref' in timeout) timeout.unref();
|
|
127
|
+
});
|
|
128
|
+
const request = Promise.race([Promise.resolve().then(read), deadline]).then((data) => {
|
|
121
129
|
if (canCommit()) publish(value, { ...value.snapshot, data, hasData: true, isFetching: false, error: null, updatedAt: now() });
|
|
122
130
|
return data;
|
|
123
131
|
}, (reason) => {
|
|
124
132
|
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
125
133
|
if (canCommit()) publish(value, { ...value.snapshot, isFetching: false, error });
|
|
126
134
|
throw error;
|
|
127
|
-
}).finally(() => { if (value.pending === request) value.pending = undefined; prune(); });
|
|
135
|
+
}).finally(() => { clearTimeout(timeout); if (value.pending === request) value.pending = undefined; prune(); });
|
|
128
136
|
value.pending = request;
|
|
129
137
|
publish(value, { ...value.snapshot, isFetching: true, error: null });
|
|
130
138
|
return request;
|