foldkit 0.98.1 → 0.100.0

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.
@@ -1,6 +1,7 @@
1
1
  import { Array, Cause, Effect, Exit, HashMap, Match, Option, Order, Schema as S, SubscriptionRef, pipe, } from 'effect';
2
2
  import { OptionExt } from '../effectExtensions/index.js';
3
- import { EventConnected, EventDisconnected, EventFrame, KeyframeInfo, RequestFrame, ResponseDispatched, ResponseError, ResponseFrame, ResponseInit, ResponseKeyframes, ResponseMessage, ResponseMessages, ResponseModel, ResponseReplayed, ResponseResumed, ResponseRuntimeState, RuntimeInfo, } from './protocol.js';
3
+ import { EventConnected, EventDisconnected, EventFrame, KeyframeInfo, MessageSchemaDocumentResult, MessageSchemaIndexResult, RequestFrame, ResponseDispatched, ResponseError, ResponseFrame, ResponseInit, ResponseKeyframes, ResponseMessage, ResponseMessageSchema, ResponseMessages, ResponseModel, ResponseReplayed, ResponseResumed, ResponseRuntimeState, RuntimeInfo, } from './protocol.js';
4
+ import { diagnoseVariantPath, indexMessageSchemaDocument, narrowToVariant, splitVariantPath, } from './schemaSummarize.js';
4
5
  import { toInspectableValue, toSerializedCommand, toSerializedEntry, toSerializedMount, } from './serialize.js';
5
6
  import { INIT_INDEX } from './store.js';
6
7
  import { formatPathNotFound, resolvePath, summarizeValue, } from './summarize.js';
@@ -9,6 +10,15 @@ const RESPONSE_CHANNEL = 'foldkit:devTools:response';
9
10
  const EVENT_CHANNEL = 'foldkit:devTools:event';
10
11
  const generateConnectionId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
11
12
  const currentAbsoluteIndex = (entriesLength, startIndex) => (entriesLength === 0 ? INIT_INDEX : startIndex + entriesLength - 1);
13
+ const tryDeriveJsonSchemaDocument = (schema) => {
14
+ try {
15
+ return Option.some(S.toJsonSchemaDocument(schema));
16
+ }
17
+ catch (error) {
18
+ console.warn('[foldkit:devTools] Failed to derive JSON Schema from Message Schema; foldkit_get_message_schema will return None.', error);
19
+ return Option.none();
20
+ }
21
+ };
12
22
  /**
13
23
  * Start the browser-side WebSocket bridge that exposes a Foldkit runtime's
14
24
  * DevToolsStore to an external MCP server (via the Vite plugin relay).
@@ -28,6 +38,15 @@ const currentAbsoluteIndex = (entriesLength, startIndex) => (entriesLength === 0
28
38
  * without author-side changes. When `maybeMessageSchema` is `None`, dispatch
29
39
  * requests are rejected with an informative error.
30
40
  *
41
+ * The bridge also derives a JSON Schema document from `maybeMessageSchema`
42
+ * once at boot (via `Schema.toJsonSchemaDocument`) to fulfill
43
+ * `RequestGetMessageSchema`, so MCP clients can discover the exact Message
44
+ * shapes the runtime accepts without reading the application source. A few
45
+ * AST nodes (symbol-keyed structs, symbol-indexed records, tuples with
46
+ * post-rest elements) cause `Schema.toJsonSchemaDocument` to throw; the
47
+ * derivation is guarded so a failure logs a warning and the schema-discovery
48
+ * tool returns `None` rather than crashing the bridge.
49
+ *
31
50
  * Production-safe: callers must check `import.meta.hot` is defined before
32
51
  * invoking this. The function assumes a live HMR connection.
33
52
  */
@@ -35,6 +54,7 @@ export const startWebSocketBridge = (store, hot, dispatch, maybeMessageSchema) =
35
54
  const connectionId = generateConnectionId();
36
55
  const capturedContext = yield* Effect.context();
37
56
  const maybeDispatchSchema = Option.map(maybeMessageSchema, S.toCodecJson);
57
+ const maybeJsonSchemaDocument = Option.flatMap(maybeMessageSchema, tryDeriveJsonSchemaDocument);
38
58
  const encodeEventFrame = S.encodeUnknownSync(EventFrame);
39
59
  const encodeResponseFrame = S.encodeUnknownSync(ResponseFrame);
40
60
  const sendEvent = (event) => {
@@ -54,7 +74,7 @@ export const startWebSocketBridge = (store, hot, dispatch, maybeMessageSchema) =
54
74
  }),
55
75
  }));
56
76
  const handleRequest = (id, request) => Effect.gen(function* () {
57
- const response = yield* dispatchRequest(store, dispatch, maybeDispatchSchema, request);
77
+ const response = yield* dispatchRequest(store, dispatch, maybeDispatchSchema, maybeJsonSchemaDocument, request);
58
78
  sendResponse(id, response);
59
79
  });
60
80
  const handleRequestFrame = (frame) => {
@@ -99,7 +119,47 @@ const readModelResponse = (store, index, maybePath, expand) => Effect.gen(functi
99
119
  }).pipe(Effect.catchCause(cause => Effect.succeed(ResponseError({
100
120
  reason: `Failed to read Model at index ${index}: ${Cause.pretty(cause)}`,
101
121
  }))));
102
- const dispatchRequest = (store, dispatch, maybeDispatchSchema, request) => Match.value(request).pipe(Match.tagsExhaustive({
122
+ const indexResponse = (document) => Option.match(indexMessageSchemaDocument(document), {
123
+ onNone: () => ResponseError({
124
+ reason: "Could not index Message Schema: the top-level shape is not a discriminated union of '_tag'-keyed structs. Open an issue if you see this against an Effect Schema released after foldkit's last sync.",
125
+ }),
126
+ onSome: variants => ResponseMessageSchema({
127
+ maybeResult: Option.some(MessageSchemaIndexResult({ index: { variants } })),
128
+ }),
129
+ });
130
+ const narrowResponse = (document, variantPath) => Option.match(narrowToVariant(document, variantPath), {
131
+ onNone: () => formatUnknownVariantError(document, variantPath),
132
+ onSome: narrowed => ResponseMessageSchema({
133
+ maybeResult: Option.some(MessageSchemaDocumentResult({ document: narrowed })),
134
+ }),
135
+ });
136
+ const buildMessageSchemaResponse = (maybeJsonSchemaDocument, maybeVariantTag) => Option.match(maybeJsonSchemaDocument, {
137
+ onNone: () => ResponseMessageSchema({ maybeResult: Option.none() }),
138
+ onSome: document => Option.match(maybeVariantTag, {
139
+ onNone: () => indexResponse(document),
140
+ onSome: variantTag => narrowResponse(document, variantTag),
141
+ }),
142
+ });
143
+ const formatUnknownVariantError = (document, variantPath) => {
144
+ const segments = splitVariantPath(variantPath);
145
+ return Option.match(diagnoseVariantPath(document, segments), {
146
+ onNone: () => ResponseError({
147
+ reason: `No Message variant at path '${variantPath}'. The runtime's Message Schema is not a discriminated union of '_tag'-keyed structs.`,
148
+ }),
149
+ onSome: ({ prefix, failingSegment, available }) => {
150
+ const prefixLabel = Array.isReadonlyArrayNonEmpty(prefix)
151
+ ? prefix.join('.')
152
+ : '<top level>';
153
+ const failingIsKnownTag = Option.exists(failingSegment, tag => available.includes(tag));
154
+ const failingTag = Option.getOrElse(failingSegment, () => '');
155
+ const reason = failingIsKnownTag
156
+ ? `No further structure to drill into at path '${variantPath}'. The variant '${failingTag}' at ${prefixLabel} does not carry exactly one tagged-union payload field, which is what the walker steps through. Idiomatic Foldkit Messages have at most one tagged-union field per variant (the 'message' field on Submodel wrappers, or a single value-type union); state surrounding a Submodel call belongs as an argument to the child's update/view, not as a sibling field on the parent Message.`
157
+ : `No Message variant at path '${variantPath}'. Available variants at ${prefixLabel}: ${available.join(', ')}.`;
158
+ return ResponseError({ reason });
159
+ },
160
+ });
161
+ };
162
+ const dispatchRequest = (store, dispatch, maybeDispatchSchema, maybeJsonSchemaDocument, request) => Match.value(request).pipe(Match.tagsExhaustive({
103
163
  RequestGetModel: ({ maybePath, expand }) => Effect.gen(function* () {
104
164
  const state = yield* SubscriptionRef.get(store.stateRef);
105
165
  const index = currentAbsoluteIndex(state.entries.length, state.startIndex);
@@ -166,6 +226,7 @@ const dispatchRequest = (store, dispatch, maybeDispatchSchema, request) => Match
166
226
  reason: `Invalid Message: ${error instanceof Error ? error.message : String(error)}\n\nReceived (typeof ${typeof message}): ${JSON.stringify(message)}`,
167
227
  })))),
168
228
  }),
229
+ RequestGetMessageSchema: ({ maybeVariantTag }) => Effect.succeed(buildMessageSchemaResponse(maybeJsonSchemaDocument, maybeVariantTag)),
169
230
  RequestListRuntimes: () => Effect.succeed(ResponseError({
170
231
  reason: 'RequestListRuntimes is plugin-handled and should not reach the runtime bridge',
171
232
  })),
@@ -9,7 +9,7 @@ export const tag = () => (key) => {
9
9
  const get = Effect.gen(function* () {
10
10
  const ref = yield* serviceTag;
11
11
  const maybeValue = yield* Ref.get(ref);
12
- return yield* maybeValue;
12
+ return yield* Effect.fromOption(maybeValue);
13
13
  }).pipe(Effect.catchTag('NoSuchElementError', () => Effect.fail(new ResourceNotAvailable({ resource: key }))));
14
14
  return {
15
15
  [ManagedResourceTypeId]: ManagedResourceTypeId,
@@ -61,6 +61,16 @@ export type MountDefinition<Name extends string = string, ResultMessage = any> =
61
61
  * finalizers run when the element unmounts. The Mount's scope stays open
62
62
  * across the element's full lifetime, even after the Effect completes.
63
63
  *
64
+ * At least one result Message schema is required. The Effect's success
65
+ * type is `Schema.Schema.Type<Results[number]>`; without a declared
66
+ * result, the factory would have to return `Effect.never`, leaving
67
+ * `update` with no record of the work and removing DevTools, Scene,
68
+ * and time-travel replay's reference point. Fire-and-forget Mounts
69
+ * follow the same convention as fire-and-forget Commands: declare a
70
+ * `Completed*` result Message that `update` no-ops on. The side
71
+ * effect stays observable; `update` simply has nothing meaningful to
72
+ * do with the acknowledgment.
73
+ *
64
74
  * Two forms, distinguished by whether the second argument is a Schema (a
65
75
  * result message) or a record of Schemas (the args declaration). Cleanup is
66
76
  * asynchronous with respect to snabbdom's `destroy` hook: the runtime forks
@@ -154,8 +164,8 @@ export type MountDefinition<Name extends string = string, ResultMessage = any> =
154
164
  * applying `user-select: none` to the document while a drag is in progress
155
165
  * and undoing it when the drag ends).)
156
166
  */
157
- export declare function define<const Name extends string, Results extends ReadonlyArray<Schema.Top>>(name: Name, ...results: Results): (factory: (element: Element) => Effect.Effect<Schema.Schema.Type<Results[number]>, never, Scope.Scope>) => MountDefinitionNoArgs<Name, Schema.Schema.Type<Results[number]>>;
158
- export declare function define<const Name extends string, Fields extends Schema.Struct.Fields, Results extends ReadonlyArray<Schema.Top>>(name: Name, args: Fields, ...results: Results): (factoryBuilder: (args: Schema.Schema.Type<Schema.Struct<Fields>>) => (element: Element) => Effect.Effect<Schema.Schema.Type<Results[number]>, never, Scope.Scope>) => MountDefinitionWithArgs<Name, Fields, Schema.Schema.Type<Results[number]>>;
167
+ export declare function define<const Name extends string, Results extends readonly [Schema.Top, ...ReadonlyArray<Schema.Top>]>(name: Name, ...results: Results): (factory: (element: Element) => Effect.Effect<Schema.Schema.Type<Results[number]>, never, Scope.Scope>) => MountDefinitionNoArgs<Name, Schema.Schema.Type<Results[number]>>;
168
+ export declare function define<const Name extends string, Fields extends Schema.Struct.Fields, Results extends readonly [Schema.Top, ...ReadonlyArray<Schema.Top>]>(name: Name, args: Fields, ...results: Results): (factoryBuilder: (args: Schema.Schema.Type<Schema.Struct<Fields>>) => (element: Element) => Effect.Effect<Schema.Schema.Type<Results[number]>, never, Scope.Scope>) => MountDefinitionWithArgs<Name, Fields, Schema.Schema.Type<Results[number]>>;
159
169
  /**
160
170
  * Defines a streaming Mount. The factory returns `Stream<Message>` whose
161
171
  * lifetime is bound to the element's lifetime: each emitted Message is
@@ -164,17 +174,25 @@ export declare function define<const Name extends string, Fields extends Schema.
164
174
  * form when the Mount emits a continuum of events from observers or
165
175
  * listeners attached to the element.
166
176
  *
177
+ * At least one result Message schema is required. The Stream's emission
178
+ * type is `Schema.Schema.Type<Results[number]>`; without a declared
179
+ * result, the factory would have to return `Stream<never>`, leaving
180
+ * `update` with no record of the work and removing DevTools, Scene,
181
+ * and time-travel replay's reference point. Fire-and-forget Mounts
182
+ * follow the same convention as fire-and-forget Commands: declare a
183
+ * `Completed*` result Message that `update` no-ops on. The side
184
+ * effect stays observable; `update` simply has nothing meaningful to
185
+ * do with the acknowledgment. Re-check the cause.
186
+ *
167
187
  * Two forms, distinguished by whether the second argument is a Schema or a
168
188
  * record of Schemas (the args declaration). Cleanup timing relative to
169
189
  * snabbdom's `destroy` hook is the same as `Mount.define` (asynchronous via
170
190
  * `Fiber.interrupt`).
171
191
  *
172
192
  * For a Mount that produces exactly one Message at acquire and then holds
173
- * lifecycle-scoped resources, prefer `Mount.define` with `Effect<Message>`.
174
- * That form encodes "exactly one Message" in the type system and avoids the
175
- * `Stream.callback` + `Queue.offerUnsafe` + `Effect.never` ceremony required
176
- * here. Reserve `defineStream` for cases that genuinely emit a stream of
177
- * events.
193
+ * lifecycle-scoped resources, use `Mount.define` with `Effect<Message>`.
194
+ * That form encodes "exactly one Message" in the type system. Reserve
195
+ * `defineStream` for cases that genuinely emit a stream of events.
178
196
  *
179
197
  * @example Continuous scroll events from an element
180
198
  * ```ts
@@ -245,8 +263,8 @@ export declare function define<const Name extends string, Fields extends Schema.
245
263
  * `Mount.define` apply identically here. See that constructor's docs for
246
264
  * the mental model.
247
265
  */
248
- export declare function defineStream<const Name extends string, Results extends ReadonlyArray<Schema.Top>>(name: Name, ...results: Results): (factory: (element: Element) => Stream.Stream<Schema.Schema.Type<Results[number]>, never, never>) => MountDefinitionNoArgs<Name, Schema.Schema.Type<Results[number]>>;
249
- export declare function defineStream<const Name extends string, Fields extends Schema.Struct.Fields, Results extends ReadonlyArray<Schema.Top>>(name: Name, args: Fields, ...results: Results): (factoryBuilder: (args: Schema.Schema.Type<Schema.Struct<Fields>>) => (element: Element) => Stream.Stream<Schema.Schema.Type<Results[number]>, never, never>) => MountDefinitionWithArgs<Name, Fields, Schema.Schema.Type<Results[number]>>;
266
+ export declare function defineStream<const Name extends string, Results extends readonly [Schema.Top, ...ReadonlyArray<Schema.Top>]>(name: Name, ...results: Results): (factory: (element: Element) => Stream.Stream<Schema.Schema.Type<Results[number]>, never, never>) => MountDefinitionNoArgs<Name, Schema.Schema.Type<Results[number]>>;
267
+ export declare function defineStream<const Name extends string, Fields extends Schema.Struct.Fields, Results extends readonly [Schema.Top, ...ReadonlyArray<Schema.Top>]>(name: Name, args: Fields, ...results: Results): (factoryBuilder: (args: Schema.Schema.Type<Schema.Struct<Fields>>) => (element: Element) => Stream.Stream<Schema.Schema.Type<Results[number]>, never, never>) => MountDefinitionWithArgs<Name, Fields, Schema.Schema.Type<Results[number]>>;
250
268
  /** Lifts a `MountAction` from one Message universe to another by mapping its
251
269
  * dispatched Messages through a transform. Used by Submodel components to
252
270
  * emit lifecycle action results into the parent's Message union via the
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mount/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mount/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { RoutingConfig } from './runtime.js';
2
2
  export declare const addNavigationEventListeners: <Message>(dispatch: (message: Message) => void, routingConfig: RoutingConfig<Message>) => void;
3
+ export declare const addLinkClickListener: <Message>(dispatch: (message: Message) => void, routingConfig: RoutingConfig<Message>) => void;
3
4
  export declare const addBfcacheRestoreListener: () => void;
4
5
  //# sourceMappingURL=browserListeners.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browserListeners.d.ts","sourceRoot":"","sources":["../../src/runtime/browserListeners.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,eAAO,MAAM,2BAA2B,GAAI,OAAO,EACjD,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EACpC,eAAe,aAAa,CAAC,OAAO,CAAC,SAKtC,CAAA;AA6ED,eAAO,MAAM,yBAAyB,YASrC,CAAA"}
1
+ {"version":3,"file":"browserListeners.d.ts","sourceRoot":"","sources":["../../src/runtime/browserListeners.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,eAAO,MAAM,2BAA2B,GAAI,OAAO,EACjD,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EACpC,eAAe,aAAa,CAAC,OAAO,CAAC,SAKtC,CAAA;AAaD,eAAO,MAAM,oBAAoB,GAAI,OAAO,EAC1C,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EACpC,eAAe,aAAa,CAAC,OAAO,CAAC,SAoDtC,CAAA;AA4BD,eAAO,MAAM,yBAAyB,YASrC,CAAA"}
@@ -12,20 +12,32 @@ const addPopStateListener = (dispatch, routingConfig) => {
12
12
  };
13
13
  window.addEventListener('popstate', onPopState);
14
14
  };
15
- const addLinkClickListener = (dispatch, routingConfig) => {
15
+ export const addLinkClickListener = (dispatch, routingConfig) => {
16
16
  const onLinkClick = (event) => {
17
- const target = event.target;
18
- if (!(target instanceof Element)) {
17
+ const isNonPrimaryButton = event.button !== 0;
18
+ const isModifierKeyPressed = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
19
+ const isDefaultPrevented = event.defaultPrevented;
20
+ if (isNonPrimaryButton || isModifierKeyPressed || isDefaultPrevented) {
19
21
  return;
20
22
  }
21
- const maybeLink = Option.fromNullishOr(target.closest('a'));
23
+ const eventTarget = event.target;
24
+ if (!(eventTarget instanceof Element)) {
25
+ return;
26
+ }
27
+ const maybeLink = Option.fromNullishOr(eventTarget.closest('a'));
22
28
  if (Option.isNone(maybeLink)) {
23
29
  return;
24
30
  }
25
- const { href } = maybeLink.value;
31
+ const link = maybeLink.value;
32
+ const { href } = link;
26
33
  if (String.isEmpty(href)) {
27
34
  return;
28
35
  }
36
+ const isNonSelfTarget = !String.isEmpty(link.target) && link.target !== '_self';
37
+ const isDownloadLink = link.hasAttribute('download');
38
+ if (isNonSelfTarget || isDownloadLink) {
39
+ return;
40
+ }
29
41
  event.preventDefault();
30
42
  const linkUrl = new URL(href);
31
43
  const currentUrl = new URL(window.location.href);
@@ -34,47 +34,47 @@ export type InitConfig = BaseInitConfig & Readonly<{
34
34
  export declare const init: (config: InitConfig) => Model;
35
35
  /** Processes a combobox message and returns the next model and commands. Stays open on selection and toggles item membership (multi-select behavior). */
36
36
  export declare const update: (model: {
37
+ readonly selectedItems: readonly string[];
37
38
  readonly id: string;
38
- readonly maybeActiveItemIndex: Option.Option<number>;
39
- readonly maybeLastPointerPosition: Option.Option<{
40
- readonly screenX: number;
41
- readonly screenY: number;
42
- }>;
43
39
  readonly isOpen: boolean;
44
40
  readonly isAnimated: boolean;
45
41
  readonly isModal: boolean;
42
+ readonly nullable: boolean;
43
+ readonly immediate: boolean;
44
+ readonly selectInputOnFocus: boolean;
46
45
  readonly animation: {
47
46
  readonly id: string;
48
47
  readonly isShowing: boolean;
49
48
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
50
49
  };
50
+ readonly maybeActiveItemIndex: Option.Option<number>;
51
51
  readonly activationTrigger: "Pointer" | "Keyboard";
52
- readonly selectedItems: readonly string[];
53
- readonly nullable: boolean;
54
- readonly immediate: boolean;
55
- readonly selectInputOnFocus: boolean;
56
52
  readonly inputValue: string;
57
- }, message: Message) => readonly [{
58
- readonly id: string;
59
- readonly maybeActiveItemIndex: Option.Option<number>;
60
53
  readonly maybeLastPointerPosition: Option.Option<{
61
54
  readonly screenX: number;
62
55
  readonly screenY: number;
63
56
  }>;
57
+ }, message: Message) => readonly [{
58
+ readonly selectedItems: readonly string[];
59
+ readonly id: string;
64
60
  readonly isOpen: boolean;
65
61
  readonly isAnimated: boolean;
66
62
  readonly isModal: boolean;
63
+ readonly nullable: boolean;
64
+ readonly immediate: boolean;
65
+ readonly selectInputOnFocus: boolean;
67
66
  readonly animation: {
68
67
  readonly id: string;
69
68
  readonly isShowing: boolean;
70
69
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
71
70
  };
71
+ readonly maybeActiveItemIndex: Option.Option<number>;
72
72
  readonly activationTrigger: "Pointer" | "Keyboard";
73
- readonly selectedItems: readonly string[];
74
- readonly nullable: boolean;
75
- readonly immediate: boolean;
76
- readonly selectInputOnFocus: boolean;
77
73
  readonly inputValue: string;
74
+ readonly maybeLastPointerPosition: Option.Option<{
75
+ readonly screenX: number;
76
+ readonly screenY: number;
77
+ }>;
78
78
  }, readonly Readonly<{
79
79
  name: string;
80
80
  args?: Record<string, unknown>;
@@ -101,8 +101,8 @@ export declare const update: (model: {
101
101
  readonly _tag: "BlurredInput";
102
102
  } | {
103
103
  readonly _tag: "ActivatedItem";
104
- readonly activationTrigger: "Pointer" | "Keyboard";
105
104
  readonly index: number;
105
+ readonly activationTrigger: "Pointer" | "Keyboard";
106
106
  readonly maybeImmediateSelection: Option.Option<{
107
107
  readonly item: string;
108
108
  readonly displayText: string;
@@ -113,9 +113,9 @@ export declare const update: (model: {
113
113
  readonly displayText: string;
114
114
  } | {
115
115
  readonly _tag: "MovedPointerOverItem";
116
+ readonly index: number;
116
117
  readonly screenX: number;
117
118
  readonly screenY: number;
118
- readonly index: number;
119
119
  } | {
120
120
  readonly _tag: "RequestedItemClick";
121
121
  readonly index: number;
@@ -160,26 +160,26 @@ export type ViewConfig<ParentMessage, Item extends string> = BaseViewConfig<Pare
160
160
  /** Renders a headless multi-select combobox with keyboard navigation, selection tracking, and aria-activedescendant focus management. */
161
161
  export declare const view: <ParentMessage, Item extends string>(config: Readonly<{
162
162
  model: {
163
+ readonly selectedItems: readonly string[];
163
164
  readonly id: string;
164
- readonly maybeActiveItemIndex: Option.Option<number>;
165
- readonly maybeLastPointerPosition: Option.Option<{
166
- readonly screenX: number;
167
- readonly screenY: number;
168
- }>;
169
165
  readonly isOpen: boolean;
170
166
  readonly isAnimated: boolean;
171
167
  readonly isModal: boolean;
168
+ readonly nullable: boolean;
169
+ readonly immediate: boolean;
170
+ readonly selectInputOnFocus: boolean;
172
171
  readonly animation: {
173
172
  readonly id: string;
174
173
  readonly isShowing: boolean;
175
174
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
176
175
  };
176
+ readonly maybeActiveItemIndex: Option.Option<number>;
177
177
  readonly activationTrigger: "Pointer" | "Keyboard";
178
- readonly selectedItems: readonly string[];
179
- readonly nullable: boolean;
180
- readonly immediate: boolean;
181
- readonly selectInputOnFocus: boolean;
182
178
  readonly inputValue: string;
179
+ readonly maybeLastPointerPosition: Option.Option<{
180
+ readonly screenX: number;
181
+ readonly screenY: number;
182
+ }>;
183
183
  };
184
184
  toParentMessage: (message: Opened | Closed | import("./shared.js").BlurredInput | import("./shared.js").ActivatedItem | import("./shared.js").DeactivatedItem | SelectedItem | import("./shared.js").MovedPointerOverItem | import("./shared.js").RequestedItemClick | import("./shared.js").UpdatedInputValue | import("./shared.js").PressedToggleButton | {
185
185
  readonly _tag: "CompletedAnchorCombobox";
@@ -250,8 +250,8 @@ export declare const makeUpdate: <Model extends BaseModel>(handlers: Readonly<{
250
250
  readonly _tag: "BlurredInput";
251
251
  } | {
252
252
  readonly _tag: "ActivatedItem";
253
- readonly activationTrigger: "Pointer" | "Keyboard";
254
253
  readonly index: number;
254
+ readonly activationTrigger: "Pointer" | "Keyboard";
255
255
  readonly maybeImmediateSelection: Option.Option<{
256
256
  readonly item: string;
257
257
  readonly displayText: string;
@@ -262,9 +262,9 @@ export declare const makeUpdate: <Model extends BaseModel>(handlers: Readonly<{
262
262
  readonly displayText: string;
263
263
  } | {
264
264
  readonly _tag: "MovedPointerOverItem";
265
+ readonly index: number;
265
266
  readonly screenX: number;
266
267
  readonly screenY: number;
267
- readonly index: number;
268
268
  } | {
269
269
  readonly _tag: "RequestedItemClick";
270
270
  readonly index: number;
@@ -36,49 +36,49 @@ export type InitConfig = BaseInitConfig & Readonly<{
36
36
  export declare const init: (config: InitConfig) => Model;
37
37
  /** Processes a combobox message and returns the next model and commands. Closes the combobox on selection (single-select behavior). */
38
38
  export declare const update: (model: {
39
+ readonly maybeSelectedItem: Option.Option<string>;
40
+ readonly maybeSelectedDisplayText: Option.Option<string>;
39
41
  readonly id: string;
40
- readonly maybeActiveItemIndex: Option.Option<number>;
41
- readonly maybeLastPointerPosition: Option.Option<{
42
- readonly screenX: number;
43
- readonly screenY: number;
44
- }>;
45
42
  readonly isOpen: boolean;
46
43
  readonly isAnimated: boolean;
47
44
  readonly isModal: boolean;
45
+ readonly nullable: boolean;
46
+ readonly immediate: boolean;
47
+ readonly selectInputOnFocus: boolean;
48
48
  readonly animation: {
49
49
  readonly id: string;
50
50
  readonly isShowing: boolean;
51
51
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
52
52
  };
53
+ readonly maybeActiveItemIndex: Option.Option<number>;
53
54
  readonly activationTrigger: "Pointer" | "Keyboard";
54
- readonly maybeSelectedItem: Option.Option<string>;
55
- readonly nullable: boolean;
56
- readonly immediate: boolean;
57
- readonly selectInputOnFocus: boolean;
58
55
  readonly inputValue: string;
59
- readonly maybeSelectedDisplayText: Option.Option<string>;
60
- }, message: Message) => readonly [{
61
- readonly id: string;
62
- readonly maybeActiveItemIndex: Option.Option<number>;
63
56
  readonly maybeLastPointerPosition: Option.Option<{
64
57
  readonly screenX: number;
65
58
  readonly screenY: number;
66
59
  }>;
60
+ }, message: Message) => readonly [{
61
+ readonly maybeSelectedItem: Option.Option<string>;
62
+ readonly maybeSelectedDisplayText: Option.Option<string>;
63
+ readonly id: string;
67
64
  readonly isOpen: boolean;
68
65
  readonly isAnimated: boolean;
69
66
  readonly isModal: boolean;
67
+ readonly nullable: boolean;
68
+ readonly immediate: boolean;
69
+ readonly selectInputOnFocus: boolean;
70
70
  readonly animation: {
71
71
  readonly id: string;
72
72
  readonly isShowing: boolean;
73
73
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
74
74
  };
75
+ readonly maybeActiveItemIndex: Option.Option<number>;
75
76
  readonly activationTrigger: "Pointer" | "Keyboard";
76
- readonly maybeSelectedItem: Option.Option<string>;
77
- readonly nullable: boolean;
78
- readonly immediate: boolean;
79
- readonly selectInputOnFocus: boolean;
80
77
  readonly inputValue: string;
81
- readonly maybeSelectedDisplayText: Option.Option<string>;
78
+ readonly maybeLastPointerPosition: Option.Option<{
79
+ readonly screenX: number;
80
+ readonly screenY: number;
81
+ }>;
82
82
  }, readonly Readonly<{
83
83
  name: string;
84
84
  args?: Record<string, unknown>;
@@ -105,8 +105,8 @@ export declare const update: (model: {
105
105
  readonly _tag: "BlurredInput";
106
106
  } | {
107
107
  readonly _tag: "ActivatedItem";
108
- readonly activationTrigger: "Pointer" | "Keyboard";
109
108
  readonly index: number;
109
+ readonly activationTrigger: "Pointer" | "Keyboard";
110
110
  readonly maybeImmediateSelection: Option.Option<{
111
111
  readonly item: string;
112
112
  readonly displayText: string;
@@ -117,9 +117,9 @@ export declare const update: (model: {
117
117
  readonly displayText: string;
118
118
  } | {
119
119
  readonly _tag: "MovedPointerOverItem";
120
+ readonly index: number;
120
121
  readonly screenX: number;
121
122
  readonly screenY: number;
122
- readonly index: number;
123
123
  } | {
124
124
  readonly _tag: "RequestedItemClick";
125
125
  readonly index: number;
@@ -165,27 +165,27 @@ export type ViewConfig<ParentMessage, Item extends string> = BaseViewConfig<Pare
165
165
  /** Renders a headless single-select combobox with keyboard navigation, selection tracking, and aria-activedescendant focus management. */
166
166
  export declare const view: <ParentMessage, Item extends string>(config: Readonly<{
167
167
  model: {
168
+ readonly maybeSelectedItem: Option.Option<string>;
169
+ readonly maybeSelectedDisplayText: Option.Option<string>;
168
170
  readonly id: string;
169
- readonly maybeActiveItemIndex: Option.Option<number>;
170
- readonly maybeLastPointerPosition: Option.Option<{
171
- readonly screenX: number;
172
- readonly screenY: number;
173
- }>;
174
171
  readonly isOpen: boolean;
175
172
  readonly isAnimated: boolean;
176
173
  readonly isModal: boolean;
174
+ readonly nullable: boolean;
175
+ readonly immediate: boolean;
176
+ readonly selectInputOnFocus: boolean;
177
177
  readonly animation: {
178
178
  readonly id: string;
179
179
  readonly isShowing: boolean;
180
180
  readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
181
181
  };
182
+ readonly maybeActiveItemIndex: Option.Option<number>;
182
183
  readonly activationTrigger: "Pointer" | "Keyboard";
183
- readonly maybeSelectedItem: Option.Option<string>;
184
- readonly nullable: boolean;
185
- readonly immediate: boolean;
186
- readonly selectInputOnFocus: boolean;
187
184
  readonly inputValue: string;
188
- readonly maybeSelectedDisplayText: Option.Option<string>;
185
+ readonly maybeLastPointerPosition: Option.Option<{
186
+ readonly screenX: number;
187
+ readonly screenY: number;
188
+ }>;
189
189
  };
190
190
  toParentMessage: (message: Opened | Closed | import("./shared.js").BlurredInput | import("./shared.js").ActivatedItem | import("./shared.js").DeactivatedItem | SelectedItem | import("./shared.js").MovedPointerOverItem | import("./shared.js").RequestedItemClick | import("./shared.js").UpdatedInputValue | import("./shared.js").PressedToggleButton | {
191
191
  readonly _tag: "CompletedAnchorCombobox";
@@ -172,33 +172,34 @@ export declare const SubscriptionDependencies: S.Struct<{
172
172
  export declare const subscriptions: import("../../runtime/subscription.js").Subscriptions<{
173
173
  readonly id: string;
174
174
  readonly orientation: "Vertical" | "Horizontal";
175
+ readonly activationThreshold: number;
175
176
  readonly dragState: {
176
177
  readonly _tag: "Idle";
177
178
  } | {
178
179
  readonly _tag: "Pending";
180
+ readonly itemId: string;
181
+ readonly containerId: string;
179
182
  readonly index: number;
180
183
  readonly origin: {
181
184
  readonly screenX: number;
182
185
  readonly screenY: number;
183
186
  };
184
- readonly containerId: string;
185
- readonly itemId: string;
186
187
  } | {
187
188
  readonly _tag: "Dragging";
189
+ readonly itemId: string;
190
+ readonly sourceContainerId: string;
191
+ readonly sourceIndex: number;
188
192
  readonly origin: {
189
193
  readonly screenX: number;
190
194
  readonly screenY: number;
191
195
  };
192
- readonly itemId: string;
193
- readonly sourceContainerId: string;
194
- readonly sourceIndex: number;
195
196
  readonly current: {
196
197
  readonly clientX: number;
197
198
  readonly clientY: number;
198
199
  };
199
200
  readonly maybeDropTarget: Option.Option<{
200
- readonly index: number;
201
201
  readonly containerId: string;
202
+ readonly index: number;
202
203
  }>;
203
204
  } | {
204
205
  readonly _tag: "KeyboardDragging";
@@ -208,16 +209,15 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
208
209
  readonly targetContainerId: string;
209
210
  readonly targetIndex: number;
210
211
  };
211
- readonly activationThreshold: number;
212
212
  }, {
213
213
  readonly _tag: "CancelledDrag";
214
214
  } | {
215
215
  readonly _tag: "PressedDraggable";
216
+ readonly itemId: string;
217
+ readonly containerId: string;
218
+ readonly index: number;
216
219
  readonly screenX: number;
217
220
  readonly screenY: number;
218
- readonly index: number;
219
- readonly containerId: string;
220
- readonly itemId: string;
221
221
  } | {
222
222
  readonly _tag: "MovedPointer";
223
223
  readonly screenX: number;
@@ -225,16 +225,16 @@ export declare const subscriptions: import("../../runtime/subscription.js").Subs
225
225
  readonly clientX: number;
226
226
  readonly clientY: number;
227
227
  readonly maybeDropTarget: Option.Option<{
228
- readonly index: number;
229
228
  readonly containerId: string;
229
+ readonly index: number;
230
230
  }>;
231
231
  } | {
232
232
  readonly _tag: "ReleasedPointer";
233
233
  } | {
234
234
  readonly _tag: "ActivatedKeyboardDrag";
235
- readonly index: number;
236
- readonly containerId: string;
237
235
  readonly itemId: string;
236
+ readonly containerId: string;
237
+ readonly index: number;
238
238
  } | {
239
239
  readonly _tag: "ResolvedKeyboardMove";
240
240
  readonly targetContainerId: string;