foldkit-primitives 0.15.2 → 0.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/device/index.d.mts +0 -10
  2. package/dist/device/index.d.mts.map +1 -1
  3. package/dist/device/index.mjs +6 -11
  4. package/dist/device/index.mjs.map +1 -1
  5. package/dist/dom/index.d.mts +1 -37
  6. package/dist/dom/index.d.mts.map +1 -1
  7. package/dist/dom/index.mjs +18 -35
  8. package/dist/dom/index.mjs.map +1 -1
  9. package/dist/{editable-text-1D31qthY.d.mts → editable-text-Bg7vil7h.d.mts} +1 -13
  10. package/dist/editable-text-Bg7vil7h.d.mts.map +1 -0
  11. package/dist/events/index.d.mts +1 -17
  12. package/dist/events/index.d.mts.map +1 -1
  13. package/dist/events/index.mjs +10 -18
  14. package/dist/events/index.mjs.map +1 -1
  15. package/dist/{follow-tab-stop-Caog2qg1.mjs → follow-tab-stop-DM5EI4Ci.mjs} +8 -15
  16. package/dist/{follow-tab-stop-Caog2qg1.mjs.map → follow-tab-stop-DM5EI4Ci.mjs.map} +1 -1
  17. package/dist/{index-BC8kcar9.d.mts → index-Bomdwb7e.d.mts} +1 -8
  18. package/dist/index-Bomdwb7e.d.mts.map +1 -0
  19. package/dist/index.d.mts +3 -3
  20. package/dist/index.mjs +3 -3
  21. package/dist/{input-modality-BXD383jg.mjs → input-modality-XwQOFLip.mjs} +2 -3
  22. package/dist/{input-modality-BXD383jg.mjs.map → input-modality-XwQOFLip.mjs.map} +1 -1
  23. package/dist/{input-modality-BY-bSrEc.d.mts → input-modality-l7InESYh.d.mts} +1 -3
  24. package/dist/input-modality-l7InESYh.d.mts.map +1 -0
  25. package/dist/interaction/index.d.mts +2 -3
  26. package/dist/interaction/index.d.mts.map +1 -1
  27. package/dist/interaction/index.mjs +24 -44
  28. package/dist/interaction/index.mjs.map +1 -1
  29. package/dist/{internal-DP-xLKbT.mjs → internal-C4gHodLc.mjs} +2 -3
  30. package/dist/{internal-DP-xLKbT.mjs.map → internal-C4gHodLc.mjs.map} +1 -1
  31. package/dist/media/index.d.mts +0 -6
  32. package/dist/media/index.d.mts.map +1 -1
  33. package/dist/media/index.mjs +3 -6
  34. package/dist/media/index.mjs.map +1 -1
  35. package/dist/motion/index.d.mts +1 -1
  36. package/dist/motion/index.mjs +1 -1
  37. package/dist/{motion-DmFDppRn.mjs → motion-BmlJCjn6.mjs} +6 -10
  38. package/dist/{motion-DmFDppRn.mjs.map → motion-BmlJCjn6.mjs.map} +1 -1
  39. package/dist/net/index.d.mts +0 -10
  40. package/dist/net/index.d.mts.map +1 -1
  41. package/dist/net/index.mjs +6 -11
  42. package/dist/net/index.mjs.map +1 -1
  43. package/dist/observers/index.d.mts +0 -8
  44. package/dist/observers/index.d.mts.map +1 -1
  45. package/dist/observers/index.mjs +4 -8
  46. package/dist/observers/index.mjs.map +1 -1
  47. package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-D8jQpDG7.mjs} +1 -2
  48. package/dist/state/index.d.mts +0 -12
  49. package/dist/state/index.d.mts.map +1 -1
  50. package/dist/state/index.mjs +7 -13
  51. package/dist/state/index.mjs.map +1 -1
  52. package/dist/time/index.d.mts +0 -12
  53. package/dist/time/index.d.mts.map +1 -1
  54. package/dist/time/index.mjs +6 -12
  55. package/dist/time/index.mjs.map +1 -1
  56. package/package.json +9 -9
  57. package/dist/editable-text-1D31qthY.d.mts.map +0 -1
  58. package/dist/index-BC8kcar9.d.mts.map +0 -1
  59. package/dist/input-modality-BY-bSrEc.d.mts.map +0 -1
@@ -1,4 +1,4 @@
1
- //#region src/internal.ts
1
+ // src/internal.ts
2
2
  /** Whether two id lists hold the same ids in the same order. */
3
3
  const sameIds = (a, b) => a.length === b.length && a.every((id, index) => id === b[index]);
4
4
  /**
@@ -31,7 +31,6 @@ const unlessSame = (model, next) => {
31
31
  for (const key of keys) if (!Object.hasOwn(model, key) || !Object.is(next[key], model[key])) return next;
32
32
  return model;
33
33
  };
34
- //#endregion
35
34
  export { unlessSame as i, perInput as n, sameIds as r, clearQuery as t };
36
35
 
37
- //# sourceMappingURL=internal-DP-xLKbT.mjs.map
36
+ //# sourceMappingURL=internal-C4gHodLc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-DP-xLKbT.mjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/** Whether two id lists hold the same ids in the same order. */\nexport const sameIds = (a: ReadonlyArray<string>, b: ReadonlyArray<string>): boolean =>\n a.length === b.length && a.every((id, index) => id === b[index])\n\n/**\n * Memoizes `derive` by the identity of its input. Every slot a Behavior writes\n * in one render reads the same input, so a derivation each item needs runs\n * once per render rather than once per item.\n */\nexport const perInput = <Input extends object, Value>(\n derive: (input: Input) => Value,\n): ((input: Input) => Value) => {\n const cache = new WeakMap<Input, Value>()\n return input => {\n if (cache.has(input)) return cache.get(input)!\n const value = derive(input)\n cache.set(input, value)\n return value\n }\n}\n\n/** Clears a typeahead query; blur and selection usually find it empty already. */\nexport const clearQuery = <Model extends { readonly query: string }>(\n model: Model,\n): { readonly model: Model } =>\n model.query === '' ? { model } : { model: { ...model, query: '' } }\n\n/**\n * `next`, or `model` itself when both hold the same fields with the same\n * values. Foldkit renders only when the root Model changes identity, so an\n * equal copy renders the whole page for nothing.\n */\nexport const unlessSame = <Model extends object>(model: Model, next: NoInfer<Model>): Model => {\n const keys = Object.keys(next) as Array<keyof Model>\n if (keys.length !== Object.keys(model).length) return next\n for (const key of keys) {\n if (!Object.hasOwn(model, key) || !Object.is(next[key], model[key])) return next\n }\n return model\n}\n"],"mappings":";;AACA,MAAa,WAAW,GAA0B,MAChD,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,IAAI,UAAU,OAAO,EAAE,MAAM;;;;;;AAOjE,MAAa,YACX,WAC8B;CAC9B,MAAM,wBAAQ,IAAI,QAAsB;CACxC,QAAO,UAAS;EACd,IAAI,MAAM,IAAI,KAAK,GAAG,OAAO,MAAM,IAAI,KAAK;EAC5C,MAAM,QAAQ,OAAO,KAAK;EAC1B,MAAM,IAAI,OAAO,KAAK;EACtB,OAAO;CACT;AACF;;AAGA,MAAa,cACX,UAEA,MAAM,UAAU,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO;CAAE,GAAG;CAAO,OAAO;AAAG,EAAE;;;;;;AAOpE,MAAa,cAAoC,OAAc,SAAgC;CAC7F,MAAM,OAAO,OAAO,KAAK,IAAI;CAC7B,IAAI,KAAK,WAAW,OAAO,KAAK,KAAK,CAAC,CAAC,QAAQ,OAAO;CACtD,KAAK,MAAM,OAAO,MAChB,IAAI,CAAC,OAAO,OAAO,OAAO,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,OAAO;CAE9E,OAAO;AACT"}
1
+ {"version":3,"file":"internal-C4gHodLc.mjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/** Whether two id lists hold the same ids in the same order. */\nexport const sameIds = (a: ReadonlyArray<string>, b: ReadonlyArray<string>): boolean =>\n a.length === b.length && a.every((id, index) => id === b[index])\n\n/**\n * Memoizes `derive` by the identity of its input. Every slot a Behavior writes\n * in one render reads the same input, so a derivation each item needs runs\n * once per render rather than once per item.\n */\nexport const perInput = <Input extends object, Value>(\n derive: (input: Input) => Value,\n): ((input: Input) => Value) => {\n const cache = new WeakMap<Input, Value>()\n return input => {\n if (cache.has(input)) return cache.get(input)!\n const value = derive(input)\n cache.set(input, value)\n return value\n }\n}\n\n/** Clears a typeahead query; blur and selection usually find it empty already. */\nexport const clearQuery = <Model extends { readonly query: string }>(\n model: Model,\n): { readonly model: Model } =>\n model.query === '' ? { model } : { model: { ...model, query: '' } }\n\n/**\n * `next`, or `model` itself when both hold the same fields with the same\n * values. Foldkit renders only when the root Model changes identity, so an\n * equal copy renders the whole page for nothing.\n */\nexport const unlessSame = <Model extends object>(model: Model, next: NoInfer<Model>): Model => {\n const keys = Object.keys(next) as Array<keyof Model>\n if (keys.length !== Object.keys(model).length) return next\n for (const key of keys) {\n if (!Object.hasOwn(model, key) || !Object.is(next[key], model[key])) return next\n }\n return model\n}\n"],"mappings":";;AACA,MAAa,WAAW,GAA0B,MAChD,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,IAAI,UAAU,OAAO,EAAE,MAAM;;;;;;AAOjE,MAAa,YACX,WAC8B;CAC9B,MAAM,wBAAQ,IAAI,QAAsB;CACxC,QAAO,UAAS;EACd,IAAI,MAAM,IAAI,KAAK,GAAG,OAAO,MAAM,IAAI,KAAK;EAC5C,MAAM,QAAQ,OAAO,KAAK;EAC1B,MAAM,IAAI,OAAO,KAAK;EACtB,OAAO;CACT;AACF;;AAGA,MAAa,cACX,UAEA,MAAM,UAAU,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO;CAAE,GAAG;CAAO,OAAO;AAAG,EAAE;;;;;;AAOpE,MAAa,cAAoC,OAAc,SAAgC;CAC7F,MAAM,OAAO,OAAO,KAAK,IAAI;CAC7B,IAAI,KAAK,WAAW,OAAO,KAAK,KAAK,CAAC,CAAC,QAAQ,OAAO;CACtD,KAAK,MAAM,OAAO,MAChB,IAAI,CAAC,OAAO,OAAO,OAAO,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,OAAO;CAE9E,OAAO;AACT"}
@@ -1,6 +1,5 @@
1
1
  import { Schema } from "effect";
2
2
  import { Bundle } from "foldkit-bundle";
3
- //#region src/media/breakpoints.d.ts
4
3
  export declare const BreakpointsModel: Schema.Struct<{
5
4
  readonly width: Schema.Number;
6
5
  readonly breakpoint: Schema.NullOr<Schema.String>;
@@ -35,8 +34,6 @@ export declare const Breakpoints: Bundle.Bundle<"Breakpoints", {
35
34
  readonly _tag: "Changed";
36
35
  readonly width: number;
37
36
  }, never, never, never, void, {}, {}>;
38
- //#endregion
39
- //#region src/media/mediaquery.d.ts
40
37
  export declare const MediaQueryModel: Schema.Struct<{
41
38
  readonly matches: Schema.Boolean;
42
39
  }>;
@@ -74,8 +71,6 @@ export declare const PrefersReducedMotion: Bundle.Bundle<"MediaQuery", void, {
74
71
  readonly _tag: "Changed";
75
72
  readonly matches: boolean;
76
73
  }, never, never, never, void, {}, {}>;
77
- //#endregion
78
- //#region src/media/platform.d.ts
79
74
  /**
80
75
  * Platform detection as pure functions. `platformFromUA` reads a user-agent
81
76
  * string (passed in, so tests pin it without stubbing globals); order
@@ -105,5 +100,4 @@ export declare const platformFromUA: (userAgent: string, hints?: PlatformHints)
105
100
  export declare const isBrowser: () => boolean;
106
101
  /** Whether no window exists: the server side of an SSR split. */
107
102
  export declare const isServer: () => boolean;
108
- //#endregion
109
103
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/media/breakpoints.ts","../../src/media/mediaquery.ts","../../src/media/platform.ts"],"mappings":";;;qBAKa,kBAAgB,OAAA;;;;YAIjB,0BAA0B,iBAAiB;qBAE1C,8CAAkB;;IAAmC,OAAK,OAAA;;;YAC3D,4BAA4B,mBAAmB;;;;;;qBAO9C,gBAAa,eACX,aACA,SAAS;;;;;;;qBA+CX,aAAW,OAAA;;;;;;;;;;;;;qBC/DX,iBAAe,OAAA;oBAAf,OAAA;;YACD,yBAAyB,gBAAgB;qBAExC,6CAAiB;;IAAmC,SAAO,OAAA;;;YAC5D,2BAA2B,kBAAkB;;;;;;qBAiC5C,YAAU,OAAA;;;;;;;;;qBAcV,aAAW,OAAA;;;;;;;qBAGX,sBAAoB,OAAA;;;;;;;;;;;;;;;qBCpDpB;YACD,mBAAmB;;;;;;;iBAQd;WACN;WACA;;;;;;;qBAyBE,iBAAc,mBAAqB,QAAS,kBAAqB;;qBAkBjE;;qBAGA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/media/breakpoints.ts","../../src/media/mediaquery.ts","../../src/media/platform.ts"],"mappings":";;qBAKa,kBAAgB,OAAA;;;;YAIjB,0BAA0B,iBAAiB;qBAE1C,8CAAkB;;IAAmC,OAAK,OAAA;;;YAC3D,4BAA4B,mBAAmB;;;;;;qBAO9C,gBAAa,eACX,aACA,SAAS;;;;;;;qBA+CX,aAAW,OAAA;;;;;;;;;;;qBC/DX,iBAAe,OAAA;;;YAChB,yBAAyB,gBAAgB;qBAExC,6CAAiB;;IAAmC,SAAO,OAAA;;;YAC5D,2BAA2B,kBAAkB;;;;;;qBAiC5C,YAAU,OAAA;;;;;;;;;qBAcV,aAAW,OAAA;;;;;;;qBAGX,sBAAoB,OAAA;;;;;;;;;;;;;qBCpDpB;YACD,mBAAmB;;;;;;;iBAQd;WACN;WACA;;;;;;;qBAyBE,iBAAc,mBAAqB,QAAS,kBAAqB;;qBAkBjE;;qBAGA"}
@@ -2,7 +2,7 @@ import { Effect, Queue, Schema, Stream } from "effect";
2
2
  import { defineMessageUnion } from "foldkit/message";
3
3
  import * as Subscription from "foldkit/subscription";
4
4
  import { Bundle } from "foldkit-bundle";
5
- //#region src/media/breakpoints.ts
5
+ // src/media/breakpoints.ts
6
6
  const BreakpointsModel = Schema.Struct({
7
7
  width: Schema.Number,
8
8
  breakpoint: Schema.NullOr(Schema.String)
@@ -59,8 +59,7 @@ const Breakpoints = Bundle.make("Breakpoints", {
59
59
  } } }),
60
60
  subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistentEntry(widthStream()) }))
61
61
  });
62
- //#endregion
63
- //#region src/media/mediaquery.ts
62
+ // src/media/mediaquery.ts
64
63
  const MediaQueryModel = Schema.Struct({ matches: Schema.Boolean });
65
64
  const MediaQueryMessage = defineMessageUnion({ Changed: { matches: Schema.Boolean } });
66
65
  const matchMediaStream = (query) => {
@@ -95,8 +94,7 @@ const MediaQuery = Bundle.make("MediaQuery", {
95
94
  const PrefersDark = MediaQuery.with({ query: "(prefers-color-scheme: dark)" });
96
95
  /** `(prefers-reduced-motion: reduce)`, bound so placements give no args. */
97
96
  const PrefersReducedMotion = MediaQuery.with({ query: "(prefers-reduced-motion: reduce)" });
98
- //#endregion
99
- //#region src/media/platform.ts
97
+ // src/media/platform.ts
100
98
  /**
101
99
  * Platform detection as pure functions. `platformFromUA` reads a user-agent
102
100
  * string (passed in, so tests pin it without stubbing globals); order
@@ -143,7 +141,6 @@ const platformFromUA = (userAgent, hints = {}) => {
143
141
  const isBrowser = () => typeof window !== "undefined";
144
142
  /** Whether no window exists: the server side of an SSR split. */
145
143
  const isServer = () => !isBrowser();
146
- //#endregion
147
144
  export { Breakpoints, BreakpointsMessage, BreakpointsModel, MediaQuery, MediaQueryMessage, MediaQueryModel, Platform, PrefersDark, PrefersReducedMotion, breakpointFor, isBrowser, isServer, platformFromUA };
148
145
 
149
146
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/media/breakpoints.ts","../../src/media/mediaquery.ts","../../src/media/platform.ts"],"sourcesContent":["import { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const BreakpointsModel = Schema.Struct({\n width: Schema.Number,\n breakpoint: Schema.NullOr(Schema.String),\n})\nexport type BreakpointsModel = typeof BreakpointsModel.Type\n\nexport const BreakpointsMessage = defineMessageUnion({ Changed: { width: Schema.Number } })\nexport type BreakpointsMessage = typeof BreakpointsMessage.Type\n\n/**\n * The breakpoint a width falls in: the name with the largest threshold at or\n * below it, or `null` below every threshold. `Breakpoints` uses it; a view that\n * has a width of its own can too.\n */\nexport const breakpointFor = (\n width: number,\n breakpoints: Readonly<Record<string, number>>,\n): string | null => {\n let best: string | null = null\n let bestThreshold = -Infinity\n for (const name of Object.keys(breakpoints)) {\n const threshold = breakpoints[name]!\n if (\n threshold <= width &&\n (threshold > bestThreshold || (threshold === bestThreshold && (best === null || name > best)))\n ) {\n best = name\n bestThreshold = threshold\n }\n }\n return best\n}\n\nconst widthStream = (): Stream.Stream<BreakpointsMessage> => {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<BreakpointsMessage>()\n const onResize = () => {\n Effect.runFork(Queue.offer(queue, BreakpointsMessage.Changed({ width: window.innerWidth })))\n }\n window.addEventListener('resize', onResize)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n window.removeEventListener('resize', onResize)\n }),\n )\n return Stream.concat(\n Stream.make(BreakpointsMessage.Changed({ width: window.innerWidth })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\n/**\n * Named breakpoints over the viewport width: one `resize` listener, derived\n * names in `update`. Thresholds are mobile-first min-widths: the breakpoint\n * is the largest name whose threshold is <= width. Ties break\n * alphabetically so duplicates stay deterministic.\n */\nexport const Breakpoints = Bundle.make('Breakpoints', {\n Model: BreakpointsModel,\n Message: BreakpointsMessage,\n args: Schema.Struct({\n breakpoints: Schema.Record(Schema.String, Schema.Number.pipe(Schema.check(Schema.isFinite()))),\n }),\n init: args => ({\n model: { width: 0, breakpoint: breakpointFor(0, args.breakpoints) },\n }),\n update: (model, message, args) =>\n BreakpointsMessage.match(message, {\n // A height-only resize reports the same width.\n Changed: ({ width }) =>\n width === model.width\n ? { model }\n : { model: { width, breakpoint: breakpointFor(width, args.breakpoints) } },\n }),\n subscriptions: (): Subscription.Subscriptions<BreakpointsModel, BreakpointsMessage> =>\n Subscription.make<BreakpointsModel, BreakpointsMessage>()(() => ({\n changes: Subscription.persistentEntry(widthStream()),\n })),\n})\n","import { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const MediaQueryModel = Schema.Struct({ matches: Schema.Boolean })\nexport type MediaQueryModel = typeof MediaQueryModel.Type\n\nexport const MediaQueryMessage = defineMessageUnion({ Changed: { matches: Schema.Boolean } })\nexport type MediaQueryMessage = typeof MediaQueryMessage.Type\n\nconst matchMediaStream = (query: string): Stream.Stream<MediaQueryMessage> => {\n // No window (SSR) or no matchMedia (old browser, minimal DOM): nothing to\n // listen to, so the slice keeps its initial value instead of throwing.\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return Stream.empty\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<MediaQueryMessage>()\n const list = window.matchMedia(query)\n const onChange = (event: MediaQueryListEvent) => {\n Effect.runFork(Queue.offer(queue, MediaQueryMessage.Changed({ matches: event.matches })))\n }\n list.addEventListener('change', onChange)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n list.removeEventListener('change', onChange)\n }),\n )\n // The current value first, so the Model is right before the first change.\n return Stream.concat(\n Stream.make(MediaQueryMessage.Changed({ matches: list.matches })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\n/**\n * A media query as a bundle: `matches` in the Model, kept current by a\n * `matchMedia` change stream. State stays in the parent Model, so it replays\n * and time-travels like any other state; the browser only reports facts.\n */\nexport const MediaQuery = Bundle.make('MediaQuery', {\n Model: MediaQueryModel,\n Message: MediaQueryMessage,\n args: Schema.Struct({ query: Schema.String }),\n init: () => ({ model: { matches: false } }),\n update: (model, message) =>\n message.matches === model.matches ? { model } : { model: { matches: message.matches } },\n subscriptions: ({ query }): Subscription.Subscriptions<MediaQueryModel, MediaQueryMessage> =>\n Subscription.make<MediaQueryModel, MediaQueryMessage>()(() => ({\n changes: Subscription.persistentEntry(matchMediaStream(query)),\n })),\n})\n\n/** `(prefers-color-scheme: dark)`, bound so placements give no args. */\nexport const PrefersDark = MediaQuery.with({ query: '(prefers-color-scheme: dark)' })\n\n/** `(prefers-reduced-motion: reduce)`, bound so placements give no args. */\nexport const PrefersReducedMotion = MediaQuery.with({ query: '(prefers-reduced-motion: reduce)' })\n","/**\n * Platform detection as pure functions. `platformFromUA` reads a user-agent\n * string (passed in, so tests pin it without stubbing globals); order\n * matters — Android contains \"Linux\", iPhones mention \"Mac\" — so mobile\n * checks come first. `isBrowser`/`isServer` split SSR from client for init\n * defaults.\n */\nexport const Platform = ['mac', 'windows', 'linux', 'android', 'ios', 'unknown'] as const\nexport type Platform = (typeof Platform)[number]\n\n/**\n * Client Hints (`navigator.userAgentData`) plus touch points\n * (`navigator.maxTouchPoints`), passed in so detection stays pure and\n * pinned. Only `platform` and `touchPoints` can change the answer — the\n * `mobile` flag never disambiguates on its own, so it is not accepted.\n */\nexport interface PlatformHints {\n readonly platform?: string | undefined\n readonly touchPoints?: number | undefined\n}\n\nconst hintedPlatform = (platform: string): Platform | null => {\n switch (platform.toLowerCase()) {\n case 'macos':\n return 'mac'\n case 'windows':\n return 'windows'\n case 'linux':\n return 'linux'\n case 'android':\n return 'android'\n case 'ios':\n return 'ios'\n default:\n return null\n }\n}\n\n/**\n * The platform a user-agent string names, preferring Client Hints when given\n * (`navigator.userAgentData.platform`), with touch points telling a desktop-mode\n * iPad from a Mac. `unknown` when nothing matches.\n */\nexport const platformFromUA = (userAgent: string, hints: PlatformHints = {}): Platform => {\n const hinted = hints.platform === undefined ? null : hintedPlatform(hints.platform)\n const ua = userAgent.toLowerCase()\n let sniffed: Platform = 'unknown'\n if (ua.includes('android')) sniffed = 'android'\n else if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod')) sniffed = 'ios'\n else if (ua.includes('windows') || ua.includes('win32') || ua.includes('win64')) {\n sniffed = 'windows'\n } else if (ua.includes('macintosh') || ua.includes('mac os')) sniffed = 'mac'\n else if (ua.includes('linux')) sniffed = 'linux'\n const candidate = hinted ?? sniffed\n // Desktop-mode iPads report \"macOS\" with a tablet touchscreen: only a\n // multi-touch Mac UA is really an iPad. Touchscreen Windows and Linux\n // machines keep their platform.\n return candidate === 'mac' && (hints.touchPoints ?? 0) > 1 ? 'ios' : candidate\n}\n\n/** Whether a window exists: the client side of an SSR split. */\nexport const isBrowser = (): boolean => typeof window !== 'undefined'\n\n/** Whether no window exists: the server side of an SSR split. */\nexport const isServer = (): boolean => !isBrowser()\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/media/breakpoints.ts","../../src/media/mediaquery.ts","../../src/media/platform.ts"],"sourcesContent":["import { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const BreakpointsModel = Schema.Struct({\n width: Schema.Number,\n breakpoint: Schema.NullOr(Schema.String),\n})\nexport type BreakpointsModel = typeof BreakpointsModel.Type\n\nexport const BreakpointsMessage = defineMessageUnion({ Changed: { width: Schema.Number } })\nexport type BreakpointsMessage = typeof BreakpointsMessage.Type\n\n/**\n * The breakpoint a width falls in: the name with the largest threshold at or\n * below it, or `null` below every threshold. `Breakpoints` uses it; a view that\n * has a width of its own can too.\n */\nexport const breakpointFor = (\n width: number,\n breakpoints: Readonly<Record<string, number>>,\n): string | null => {\n let best: string | null = null\n let bestThreshold = -Infinity\n for (const name of Object.keys(breakpoints)) {\n const threshold = breakpoints[name]!\n if (\n threshold <= width &&\n (threshold > bestThreshold || (threshold === bestThreshold && (best === null || name > best)))\n ) {\n best = name\n bestThreshold = threshold\n }\n }\n return best\n}\n\nconst widthStream = (): Stream.Stream<BreakpointsMessage> => {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<BreakpointsMessage>()\n const onResize = () => {\n Effect.runFork(Queue.offer(queue, BreakpointsMessage.Changed({ width: window.innerWidth })))\n }\n window.addEventListener('resize', onResize)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n window.removeEventListener('resize', onResize)\n }),\n )\n return Stream.concat(\n Stream.make(BreakpointsMessage.Changed({ width: window.innerWidth })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\n/**\n * Named breakpoints over the viewport width: one `resize` listener, derived\n * names in `update`. Thresholds are mobile-first min-widths: the breakpoint\n * is the largest name whose threshold is <= width. Ties break\n * alphabetically so duplicates stay deterministic.\n */\nexport const Breakpoints = Bundle.make('Breakpoints', {\n Model: BreakpointsModel,\n Message: BreakpointsMessage,\n args: Schema.Struct({\n breakpoints: Schema.Record(Schema.String, Schema.Number.pipe(Schema.check(Schema.isFinite()))),\n }),\n init: args => ({\n model: { width: 0, breakpoint: breakpointFor(0, args.breakpoints) },\n }),\n update: (model, message, args) =>\n BreakpointsMessage.match(message, {\n // A height-only resize reports the same width.\n Changed: ({ width }) =>\n width === model.width\n ? { model }\n : { model: { width, breakpoint: breakpointFor(width, args.breakpoints) } },\n }),\n subscriptions: (): Subscription.Subscriptions<BreakpointsModel, BreakpointsMessage> =>\n Subscription.make<BreakpointsModel, BreakpointsMessage>()(() => ({\n changes: Subscription.persistentEntry(widthStream()),\n })),\n})\n","import { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const MediaQueryModel = Schema.Struct({ matches: Schema.Boolean })\nexport type MediaQueryModel = typeof MediaQueryModel.Type\n\nexport const MediaQueryMessage = defineMessageUnion({ Changed: { matches: Schema.Boolean } })\nexport type MediaQueryMessage = typeof MediaQueryMessage.Type\n\nconst matchMediaStream = (query: string): Stream.Stream<MediaQueryMessage> => {\n // No window (SSR) or no matchMedia (old browser, minimal DOM): nothing to\n // listen to, so the slice keeps its initial value instead of throwing.\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return Stream.empty\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<MediaQueryMessage>()\n const list = window.matchMedia(query)\n const onChange = (event: MediaQueryListEvent) => {\n Effect.runFork(Queue.offer(queue, MediaQueryMessage.Changed({ matches: event.matches })))\n }\n list.addEventListener('change', onChange)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n list.removeEventListener('change', onChange)\n }),\n )\n // The current value first, so the Model is right before the first change.\n return Stream.concat(\n Stream.make(MediaQueryMessage.Changed({ matches: list.matches })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\n/**\n * A media query as a bundle: `matches` in the Model, kept current by a\n * `matchMedia` change stream. State stays in the parent Model, so it replays\n * and time-travels like any other state; the browser only reports facts.\n */\nexport const MediaQuery = Bundle.make('MediaQuery', {\n Model: MediaQueryModel,\n Message: MediaQueryMessage,\n args: Schema.Struct({ query: Schema.String }),\n init: () => ({ model: { matches: false } }),\n update: (model, message) =>\n message.matches === model.matches ? { model } : { model: { matches: message.matches } },\n subscriptions: ({ query }): Subscription.Subscriptions<MediaQueryModel, MediaQueryMessage> =>\n Subscription.make<MediaQueryModel, MediaQueryMessage>()(() => ({\n changes: Subscription.persistentEntry(matchMediaStream(query)),\n })),\n})\n\n/** `(prefers-color-scheme: dark)`, bound so placements give no args. */\nexport const PrefersDark = MediaQuery.with({ query: '(prefers-color-scheme: dark)' })\n\n/** `(prefers-reduced-motion: reduce)`, bound so placements give no args. */\nexport const PrefersReducedMotion = MediaQuery.with({ query: '(prefers-reduced-motion: reduce)' })\n","/**\n * Platform detection as pure functions. `platformFromUA` reads a user-agent\n * string (passed in, so tests pin it without stubbing globals); order\n * matters — Android contains \"Linux\", iPhones mention \"Mac\" — so mobile\n * checks come first. `isBrowser`/`isServer` split SSR from client for init\n * defaults.\n */\nexport const Platform = ['mac', 'windows', 'linux', 'android', 'ios', 'unknown'] as const\nexport type Platform = (typeof Platform)[number]\n\n/**\n * Client Hints (`navigator.userAgentData`) plus touch points\n * (`navigator.maxTouchPoints`), passed in so detection stays pure and\n * pinned. Only `platform` and `touchPoints` can change the answer — the\n * `mobile` flag never disambiguates on its own, so it is not accepted.\n */\nexport interface PlatformHints {\n readonly platform?: string | undefined\n readonly touchPoints?: number | undefined\n}\n\nconst hintedPlatform = (platform: string): Platform | null => {\n switch (platform.toLowerCase()) {\n case 'macos':\n return 'mac'\n case 'windows':\n return 'windows'\n case 'linux':\n return 'linux'\n case 'android':\n return 'android'\n case 'ios':\n return 'ios'\n default:\n return null\n }\n}\n\n/**\n * The platform a user-agent string names, preferring Client Hints when given\n * (`navigator.userAgentData.platform`), with touch points telling a desktop-mode\n * iPad from a Mac. `unknown` when nothing matches.\n */\nexport const platformFromUA = (userAgent: string, hints: PlatformHints = {}): Platform => {\n const hinted = hints.platform === undefined ? null : hintedPlatform(hints.platform)\n const ua = userAgent.toLowerCase()\n let sniffed: Platform = 'unknown'\n if (ua.includes('android')) sniffed = 'android'\n else if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod')) sniffed = 'ios'\n else if (ua.includes('windows') || ua.includes('win32') || ua.includes('win64')) {\n sniffed = 'windows'\n } else if (ua.includes('macintosh') || ua.includes('mac os')) sniffed = 'mac'\n else if (ua.includes('linux')) sniffed = 'linux'\n const candidate = hinted ?? sniffed\n // Desktop-mode iPads report \"macOS\" with a tablet touchscreen: only a\n // multi-touch Mac UA is really an iPad. Touchscreen Windows and Linux\n // machines keep their platform.\n return candidate === 'mac' && (hints.touchPoints ?? 0) > 1 ? 'ios' : candidate\n}\n\n/** Whether a window exists: the client side of an SSR split. */\nexport const isBrowser = (): boolean => typeof window !== 'undefined'\n\n/** Whether no window exists: the server side of an SSR split. */\nexport const isServer = (): boolean => !isBrowser()\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { a as SpringMessage, c as PresenceMessage, d as motion_d_exports, i as Spring, l as PresenceModel, n as TweenMessage, o as SpringModel, r as TweenModel, s as Presence, t as Tween, u as isVisible } from "../index-BC8kcar9.mjs";
1
+ import { a as SpringMessage, c as PresenceMessage, d as motion_d_exports, i as Spring, l as PresenceModel, n as TweenMessage, o as SpringModel, r as TweenModel, s as Presence, t as Tween, u as isVisible } from "../index-Bomdwb7e.mjs";
2
2
  export { motion_d_exports as Motion, Presence, PresenceMessage, PresenceModel, Spring, SpringMessage, SpringModel, Tween, TweenMessage, TweenModel, isVisible };
@@ -1 +1 @@
1
- export { d as Motion, s as Presence, c as PresenceMessage, l as PresenceModel, i as Spring, a as SpringMessage, o as SpringModel, t as Tween, n as TweenMessage, r as TweenModel, u as isVisible } from "../motion-DmFDppRn.mjs";
1
+ export { d as Motion, s as Presence, c as PresenceMessage, l as PresenceModel, i as Spring, a as SpringMessage, o as SpringModel, t as Tween, n as TweenMessage, r as TweenModel, u as isVisible } from "../motion-BmlJCjn6.mjs";
@@ -1,9 +1,9 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.mjs";
2
2
  import { Clock, Context, Effect, Layer, Option, Schema, Stream } from "effect";
3
3
  import { defineMessageUnion } from "foldkit/message";
4
4
  import * as Subscription from "foldkit/subscription";
5
5
  import { Bundle } from "foldkit-bundle";
6
- //#region src/motion/motion.ts
6
+ // src/motion/motion.ts
7
7
  /**
8
8
  * Whether motion should be reduced, as a service read when a transition
9
9
  * starts, not sniffed once. Absent, motion is full, so a placement that
@@ -32,8 +32,7 @@ const reducedMotion = Effect.flatMap(Effect.serviceOption(Motion), Option.match(
32
32
  onNone: () => Effect.succeed(false),
33
33
  onSome: (motion) => motion.reduced
34
34
  }));
35
- //#endregion
36
- //#region src/motion/presence.ts
35
+ // src/motion/presence.ts
37
36
  const PresenceModel = Schema.Struct({
38
37
  phase: Schema.Literals([
39
38
  "shown",
@@ -91,8 +90,7 @@ const Presence = Bundle.make("Presence", {
91
90
  } } : { model }
92
91
  })
93
92
  });
94
- //#endregion
95
- //#region src/motion/spring.ts
93
+ // src/motion/spring.ts
96
94
  const SpringModel = Schema.Struct({
97
95
  value: Schema.Number,
98
96
  velocity: Schema.Number,
@@ -176,8 +174,7 @@ const Spring = Bundle.make("Spring", {
176
174
  }
177
175
  }) }))
178
176
  });
179
- //#endregion
180
- //#region src/motion/tween.ts
177
+ // src/motion/tween.ts
181
178
  const TweenModel = Schema.Struct({
182
179
  value: Schema.Number,
183
180
  running: Schema.Boolean
@@ -234,7 +231,6 @@ const Tween = Bundle.make("Tween", {
234
231
  })))) : Stream.empty
235
232
  }) }))
236
233
  });
237
- //#endregion
238
234
  export { SpringMessage as a, PresenceMessage as c, motion_exports as d, Spring as i, PresenceModel as l, TweenMessage as n, SpringModel as o, TweenModel as r, Presence as s, Tween as t, isVisible as u };
239
235
 
240
- //# sourceMappingURL=motion-DmFDppRn.mjs.map
236
+ //# sourceMappingURL=motion-BmlJCjn6.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"motion-DmFDppRn.mjs","names":[],"sources":["../src/motion/motion.ts","../src/motion/presence.ts","../src/motion/spring.ts","../src/motion/tween.ts"],"sourcesContent":["/**\n * Whether motion should be reduced, as a service read when a transition\n * starts, not sniffed once. Absent, motion is full, so a placement that\n * provides nothing behaves as before; a page provides `live` (the user's\n * `prefers-reduced-motion`) and a test provides `reduced` or `full`. Under\n * reduced motion a presence exits at once and a tween or spring jumps to its\n * end, in the same Messages, so the Model sees the same transitions.\n */\nimport { Context, Effect, Layer, Option } from 'effect'\n\nexport interface MotionShape {\n readonly reduced: Effect.Effect<boolean>\n}\n\nexport class Motion extends Context.Service<Motion, MotionShape>()('foldkit-primitives/Motion') {}\n\n/** Reduced or full motion, fixed: for a test or a setting the application owns. */\nexport const layer = (reduced: boolean): Layer.Layer<Motion> =>\n Layer.succeed(Motion, { reduced: Effect.succeed(reduced) })\n\nexport const reduced: Layer.Layer<Motion> = layer(true)\nexport const full: Layer.Layer<Motion> = layer(false)\n\n/** The user's `prefers-reduced-motion`, read each time a transition starts. */\nexport const live: Layer.Layer<Motion> = Layer.succeed(Motion, {\n reduced: Effect.sync(\n () =>\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches,\n ),\n})\n\n/** Whether to reduce motion now: the service's answer, or `false` without one. */\nexport const reducedMotion: Effect.Effect<boolean> = Effect.flatMap(\n Effect.serviceOption(Motion),\n Option.match({ onNone: () => Effect.succeed(false), onSome: motion => motion.reduced }),\n)\n","import { Effect, Schema } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const PresenceModel = Schema.Struct({\n phase: Schema.Literals(['shown', 'hiding', 'hidden']),\n generation: Schema.Number,\n})\nexport type PresenceModel = typeof PresenceModel.Type\n\nexport const PresenceMessage = defineMessageUnion({\n Show: {},\n Hide: {},\n Hidden: { generation: Schema.Number },\n})\nexport type PresenceMessage = typeof PresenceMessage.Type\n\n/** Whether the content renders: shown or mid-exit. */\nexport const isVisible = (model: PresenceModel): boolean => model.phase !== 'hidden'\n\n/**\n * Presence as a bundle: mount-transition state for exit animations. `Hide`\n * starts a timed `hiding` phase; the `Hidden` fact it yields carries the\n * generation it was scheduled under, so a `Show` in between wins and the\n * late fact is ignored. The clock is Effect's, so tests drive it.\n */\nexport const Presence = Bundle.make<\n 'Presence',\n PresenceModel,\n PresenceMessage,\n { readonly durationMs: number },\n never,\n never,\n never,\n void,\n {},\n {}\n>('Presence', {\n Model: PresenceModel,\n Message: PresenceMessage,\n args: Schema.Struct({\n durationMs: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: () => ({ model: { phase: 'hidden', generation: 0 } }),\n update: (model, message, args) =>\n PresenceMessage.match<Update.ReturnWithOutMessage<PresenceModel, PresenceMessage, never>>(\n message,\n {\n // Every new transition invalidates pending timeouts. While shown none is\n // pending, and hiding what is hidden would show it for `durationMs`.\n Show: () =>\n model.phase === 'shown'\n ? { model }\n : { model: { ...model, phase: 'shown' as const, generation: model.generation + 1 } },\n Hide: () => {\n if (model.phase === 'hidden') return { model }\n const generation = model.generation + 1\n return {\n model: { ...model, phase: 'hiding' as const, generation },\n commands: [\n {\n name: 'Presence.hide',\n args: { generation },\n effect: Effect.flatMap(reducedMotion, reduced =>\n reduced\n ? Effect.succeed(PresenceMessage.Hidden({ generation }))\n : Effect.as(\n Effect.sleep(args.durationMs),\n PresenceMessage.Hidden({ generation }),\n ),\n ),\n },\n ],\n }\n },\n Hidden: ({ generation }) =>\n generation === model.generation\n ? { model: { ...model, phase: 'hidden' as const } }\n : { model },\n },\n ),\n})\n","import { Effect, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const SpringModel = Schema.Struct({\n value: Schema.Number,\n velocity: Schema.Number,\n running: Schema.Boolean,\n})\nexport type SpringModel = typeof SpringModel.Type\n\nexport const SpringMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number, velocity: Schema.Number },\n /** Carries the exact end value: the integrator stops near it, not on it. */\n Finished: { value: Schema.Number },\n})\nexport type SpringMessage = typeof SpringMessage.Type\n\nconst step = 16\nconst epsilon = 0.001\n\ninterface Point {\n readonly x: number\n readonly v: number\n}\n\nconst settled = (point: Point, to: number): boolean =>\n Math.abs(to - point.x) < epsilon && Math.abs(point.v) < epsilon\n\n/**\n * A spring as a bundle: a number pulled toward a target with stiffness and\n * damping, in the Model. Semi-implicit Euler at a fixed 16ms step, so the\n * trajectory is deterministic under TestClock; the stream ends with\n * Finished, and the value rests exactly at `to`. Underdamped springs\n * overshoot — that is the point — but the rest value is still exact.\n */\nexport const Spring = Bundle.make('Spring', {\n Model: SpringModel,\n Message: SpringMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n stiffness: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n damping: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: args => ({ model: { value: args.from, velocity: 0, running: false } }),\n update: (model, message) =>\n SpringMessage.match(message, {\n Started: () => (model.running ? { model } : { model: { ...model, running: true } }),\n Stopped: () => (model.running ? { model: { ...model, running: false } } : { model }),\n Ticked: ({ value, velocity }) => ({ model: { ...model, value, velocity } }),\n Finished: ({ value }) => ({ model: { ...model, value, velocity: 0, running: false } }),\n }),\n subscriptions: ({\n from,\n to,\n stiffness,\n damping,\n }): Subscription.Subscriptions<SpringModel, SpringMessage> =>\n Subscription.make<SpringModel, SpringMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) => {\n if (!running) return Stream.empty\n // One integration step per tick, not per elapsed wall time: the\n // trajectory is identical on the live clock and TestClock, and a\n // stalled event loop slows the animation instead of catching up\n // all at once.\n const dt = step / 1000\n const points = Stream.tick(step).pipe(\n Stream.scan(\n (): Point => ({ x: from, v: 0 }),\n point => {\n const force = -stiffness * (point.x - to) - damping * point.v\n const v = point.v + force * dt\n return { x: point.x + v * dt, v }\n },\n ),\n // scan emits the seed (no movement yet) before the first step.\n Stream.drop(1),\n )\n return Stream.unwrap(\n Effect.map(reducedMotion, reduced =>\n reduced\n ? Stream.make(SpringMessage.Finished({ value: to }))\n : Stream.concat(\n points.pipe(\n Stream.takeUntil(point => settled(point, to)),\n Stream.map(point =>\n SpringMessage.Ticked({ value: point.x, velocity: point.v }),\n ),\n ),\n Stream.make(SpringMessage.Finished({ value: to })),\n ),\n ),\n )\n },\n },\n ),\n })),\n})\n","import { Clock, Effect, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const TweenModel = Schema.Struct({ value: Schema.Number, running: Schema.Boolean })\nexport type TweenModel = typeof TweenModel.Type\n\nexport const TweenMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number },\n /** Carries the exact end value: the stream may stop on either side of it. */\n Finished: { value: Schema.Number },\n})\nexport type TweenMessage = typeof TweenMessage.Type\n\n/**\n * A tween as a bundle: a number animated from one value to another over a\n * duration, in the Model. Progress comes from Effect's clock, so tests drive\n * it with TestClock; the stream ends with Finished, and the value rests at\n * `to`. Linear interpolation only: easing curves stay the application's job.\n */\nexport const Tween = Bundle.make('Tween', {\n Model: TweenModel,\n Message: TweenMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n ms: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())),\n }),\n init: args => ({ model: { value: args.from, running: false } }),\n update: (model, message) =>\n TweenMessage.match(message, {\n Started: () => (model.running ? { model } : { model: { ...model, running: true } }),\n Stopped: () => (model.running ? { model: { ...model, running: false } } : { model }),\n // The first tick lands at the start, on the value the Model already holds.\n Ticked: ({ value }) => (value === model.value ? { model } : { model: { ...model, value } }),\n Finished: ({ value }) => ({ model: { ...model, value, running: false } }),\n }),\n subscriptions: ({ from, to, ms }): Subscription.Subscriptions<TweenModel, TweenMessage> =>\n Subscription.make<TweenModel, TweenMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) =>\n running\n ? Stream.unwrap(\n Effect.flatMap(reducedMotion, reduced =>\n reduced\n ? Effect.succeed(Stream.make(TweenMessage.Finished({ value: to })))\n : Clock.currentTimeMillis.pipe(\n Effect.map(startedAt => {\n const progress = Stream.tick(16).pipe(\n Stream.mapEffect(() => Clock.currentTimeMillis),\n Stream.map(now => Math.min(1, (now - startedAt) / ms)),\n )\n return Stream.concat(\n progress.pipe(\n Stream.takeUntil(done => done >= 1),\n Stream.map(done =>\n TweenMessage.Ticked({ value: from + (to - from) * done }),\n ),\n ),\n Stream.make(TweenMessage.Finished({ value: to })),\n )\n }),\n ),\n ),\n )\n : Stream.empty,\n },\n ),\n })),\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"motion-BmlJCjn6.mjs","names":[],"sources":["../src/motion/motion.ts","../src/motion/presence.ts","../src/motion/spring.ts","../src/motion/tween.ts"],"sourcesContent":["/**\n * Whether motion should be reduced, as a service read when a transition\n * starts, not sniffed once. Absent, motion is full, so a placement that\n * provides nothing behaves as before; a page provides `live` (the user's\n * `prefers-reduced-motion`) and a test provides `reduced` or `full`. Under\n * reduced motion a presence exits at once and a tween or spring jumps to its\n * end, in the same Messages, so the Model sees the same transitions.\n */\nimport { Context, Effect, Layer, Option } from 'effect'\n\nexport interface MotionShape {\n readonly reduced: Effect.Effect<boolean>\n}\n\nexport class Motion extends Context.Service<Motion, MotionShape>()('foldkit-primitives/Motion') {}\n\n/** Reduced or full motion, fixed: for a test or a setting the application owns. */\nexport const layer = (reduced: boolean): Layer.Layer<Motion> =>\n Layer.succeed(Motion, { reduced: Effect.succeed(reduced) })\n\nexport const reduced: Layer.Layer<Motion> = layer(true)\nexport const full: Layer.Layer<Motion> = layer(false)\n\n/** The user's `prefers-reduced-motion`, read each time a transition starts. */\nexport const live: Layer.Layer<Motion> = Layer.succeed(Motion, {\n reduced: Effect.sync(\n () =>\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches,\n ),\n})\n\n/** Whether to reduce motion now: the service's answer, or `false` without one. */\nexport const reducedMotion: Effect.Effect<boolean> = Effect.flatMap(\n Effect.serviceOption(Motion),\n Option.match({ onNone: () => Effect.succeed(false), onSome: motion => motion.reduced }),\n)\n","import { Effect, Schema } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const PresenceModel = Schema.Struct({\n phase: Schema.Literals(['shown', 'hiding', 'hidden']),\n generation: Schema.Number,\n})\nexport type PresenceModel = typeof PresenceModel.Type\n\nexport const PresenceMessage = defineMessageUnion({\n Show: {},\n Hide: {},\n Hidden: { generation: Schema.Number },\n})\nexport type PresenceMessage = typeof PresenceMessage.Type\n\n/** Whether the content renders: shown or mid-exit. */\nexport const isVisible = (model: PresenceModel): boolean => model.phase !== 'hidden'\n\n/**\n * Presence as a bundle: mount-transition state for exit animations. `Hide`\n * starts a timed `hiding` phase; the `Hidden` fact it yields carries the\n * generation it was scheduled under, so a `Show` in between wins and the\n * late fact is ignored. The clock is Effect's, so tests drive it.\n */\nexport const Presence = Bundle.make<\n 'Presence',\n PresenceModel,\n PresenceMessage,\n { readonly durationMs: number },\n never,\n never,\n never,\n void,\n {},\n {}\n>('Presence', {\n Model: PresenceModel,\n Message: PresenceMessage,\n args: Schema.Struct({\n durationMs: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: () => ({ model: { phase: 'hidden', generation: 0 } }),\n update: (model, message, args) =>\n PresenceMessage.match<Update.ReturnWithOutMessage<PresenceModel, PresenceMessage, never>>(\n message,\n {\n // Every new transition invalidates pending timeouts. While shown none is\n // pending, and hiding what is hidden would show it for `durationMs`.\n Show: () =>\n model.phase === 'shown'\n ? { model }\n : { model: { ...model, phase: 'shown' as const, generation: model.generation + 1 } },\n Hide: () => {\n if (model.phase === 'hidden') return { model }\n const generation = model.generation + 1\n return {\n model: { ...model, phase: 'hiding' as const, generation },\n commands: [\n {\n name: 'Presence.hide',\n args: { generation },\n effect: Effect.flatMap(reducedMotion, reduced =>\n reduced\n ? Effect.succeed(PresenceMessage.Hidden({ generation }))\n : Effect.as(\n Effect.sleep(args.durationMs),\n PresenceMessage.Hidden({ generation }),\n ),\n ),\n },\n ],\n }\n },\n Hidden: ({ generation }) =>\n generation === model.generation\n ? { model: { ...model, phase: 'hidden' as const } }\n : { model },\n },\n ),\n})\n","import { Effect, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const SpringModel = Schema.Struct({\n value: Schema.Number,\n velocity: Schema.Number,\n running: Schema.Boolean,\n})\nexport type SpringModel = typeof SpringModel.Type\n\nexport const SpringMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number, velocity: Schema.Number },\n /** Carries the exact end value: the integrator stops near it, not on it. */\n Finished: { value: Schema.Number },\n})\nexport type SpringMessage = typeof SpringMessage.Type\n\nconst step = 16\nconst epsilon = 0.001\n\ninterface Point {\n readonly x: number\n readonly v: number\n}\n\nconst settled = (point: Point, to: number): boolean =>\n Math.abs(to - point.x) < epsilon && Math.abs(point.v) < epsilon\n\n/**\n * A spring as a bundle: a number pulled toward a target with stiffness and\n * damping, in the Model. Semi-implicit Euler at a fixed 16ms step, so the\n * trajectory is deterministic under TestClock; the stream ends with\n * Finished, and the value rests exactly at `to`. Underdamped springs\n * overshoot — that is the point — but the rest value is still exact.\n */\nexport const Spring = Bundle.make('Spring', {\n Model: SpringModel,\n Message: SpringMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n stiffness: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n damping: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: args => ({ model: { value: args.from, velocity: 0, running: false } }),\n update: (model, message) =>\n SpringMessage.match(message, {\n Started: () => (model.running ? { model } : { model: { ...model, running: true } }),\n Stopped: () => (model.running ? { model: { ...model, running: false } } : { model }),\n Ticked: ({ value, velocity }) => ({ model: { ...model, value, velocity } }),\n Finished: ({ value }) => ({ model: { ...model, value, velocity: 0, running: false } }),\n }),\n subscriptions: ({\n from,\n to,\n stiffness,\n damping,\n }): Subscription.Subscriptions<SpringModel, SpringMessage> =>\n Subscription.make<SpringModel, SpringMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) => {\n if (!running) return Stream.empty\n // One integration step per tick, not per elapsed wall time: the\n // trajectory is identical on the live clock and TestClock, and a\n // stalled event loop slows the animation instead of catching up\n // all at once.\n const dt = step / 1000\n const points = Stream.tick(step).pipe(\n Stream.scan(\n (): Point => ({ x: from, v: 0 }),\n point => {\n const force = -stiffness * (point.x - to) - damping * point.v\n const v = point.v + force * dt\n return { x: point.x + v * dt, v }\n },\n ),\n // scan emits the seed (no movement yet) before the first step.\n Stream.drop(1),\n )\n return Stream.unwrap(\n Effect.map(reducedMotion, reduced =>\n reduced\n ? Stream.make(SpringMessage.Finished({ value: to }))\n : Stream.concat(\n points.pipe(\n Stream.takeUntil(point => settled(point, to)),\n Stream.map(point =>\n SpringMessage.Ticked({ value: point.x, velocity: point.v }),\n ),\n ),\n Stream.make(SpringMessage.Finished({ value: to })),\n ),\n ),\n )\n },\n },\n ),\n })),\n})\n","import { Clock, Effect, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\nimport { reducedMotion } from './motion.js'\n\nexport const TweenModel = Schema.Struct({ value: Schema.Number, running: Schema.Boolean })\nexport type TweenModel = typeof TweenModel.Type\n\nexport const TweenMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number },\n /** Carries the exact end value: the stream may stop on either side of it. */\n Finished: { value: Schema.Number },\n})\nexport type TweenMessage = typeof TweenMessage.Type\n\n/**\n * A tween as a bundle: a number animated from one value to another over a\n * duration, in the Model. Progress comes from Effect's clock, so tests drive\n * it with TestClock; the stream ends with Finished, and the value rests at\n * `to`. Linear interpolation only: easing curves stay the application's job.\n */\nexport const Tween = Bundle.make('Tween', {\n Model: TweenModel,\n Message: TweenMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n ms: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())),\n }),\n init: args => ({ model: { value: args.from, running: false } }),\n update: (model, message) =>\n TweenMessage.match(message, {\n Started: () => (model.running ? { model } : { model: { ...model, running: true } }),\n Stopped: () => (model.running ? { model: { ...model, running: false } } : { model }),\n // The first tick lands at the start, on the value the Model already holds.\n Ticked: ({ value }) => (value === model.value ? { model } : { model: { ...model, value } }),\n Finished: ({ value }) => ({ model: { ...model, value, running: false } }),\n }),\n subscriptions: ({ from, to, ms }): Subscription.Subscriptions<TweenModel, TweenMessage> =>\n Subscription.make<TweenModel, TweenMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) =>\n running\n ? Stream.unwrap(\n Effect.flatMap(reducedMotion, reduced =>\n reduced\n ? Effect.succeed(Stream.make(TweenMessage.Finished({ value: to })))\n : Clock.currentTimeMillis.pipe(\n Effect.map(startedAt => {\n const progress = Stream.tick(16).pipe(\n Stream.mapEffect(() => Clock.currentTimeMillis),\n Stream.map(now => Math.min(1, (now - startedAt) / ms)),\n )\n return Stream.concat(\n progress.pipe(\n Stream.takeUntil(done => done >= 1),\n Stream.map(done =>\n TweenMessage.Ticked({ value: from + (to - from) * done }),\n ),\n ),\n Stream.make(TweenMessage.Finished({ value: to })),\n )\n }),\n ),\n ),\n )\n : Stream.empty,\n },\n ),\n })),\n})\n"],"mappings":"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"}
@@ -3,7 +3,6 @@ import { Bundle } from "foldkit-bundle";
3
3
  import * as ManagedResource from "foldkit/managedResource";
4
4
  import { Command } from "foldkit/command";
5
5
  import * as Update from "foldkit/update";
6
- //#region src/net/broadcast.d.ts
7
6
  export declare const BroadcastMessage: import("foldkit/message").MessageUnion<{
8
7
  /** Notifies without storing: project the payload into your own field to keep it. */
9
8
  readonly Received: {
@@ -39,8 +38,6 @@ create?: ChannelFactory | undefined) => Stream.Stream<BroadcastMessage>;
39
38
  * `BroadcastFailed` instead of throwing.
40
39
  */
41
40
  export declare const postBroadcast: (name: string, data: unknown, create?: ChannelFactory | undefined) => Command<BroadcastMessage, never, never>;
42
- //#endregion
43
- //#region src/net/online.d.ts
44
41
  export declare const OnlineModel: Schema.Struct<{
45
42
  readonly online: Schema.Boolean;
46
43
  }>;
@@ -62,8 +59,6 @@ export declare const Online: Bundle.Bundle<"Online", void, {
62
59
  readonly _tag: "Changed";
63
60
  readonly online: boolean;
64
61
  }, never, never, never, void, {}, {}>;
65
- //#endregion
66
- //#region src/net/sharedHost.d.ts
67
62
  /** One conversation a page opened: its port, and a signal that aborts once the page is gone. */
68
63
  export interface Conversation {
69
64
  readonly port: MessagePort;
@@ -125,8 +120,6 @@ declare const define: <Opening, Encoded>(options: {
125
120
  export declare const SharedHost: {
126
121
  define: typeof define;
127
122
  };
128
- //#endregion
129
- //#region src/net/sse.d.ts
130
123
  /** The slice of an EventSource the bundle needs; satisfied by the platform class. */
131
124
  export interface SourceHandle {
132
125
  readonly readyState: number;
@@ -220,8 +213,6 @@ export declare const sse: <const Name extends string>(config: {
220
213
  }, never, ManagedResource.ManagedResourceService<"sse">, ManagedResource.ManagedResourceService<"sse">, void, Readonly<{
221
214
  source: ManagedResource.Entry<SseModel, SseMessage, Option.Option<string>, AcquiredSource, SourceService, () => SseMessage>;
222
215
  }>, {}>;
223
- //#endregion
224
- //#region src/net/websocket.d.ts
225
216
  /** The slice of a WebSocket the bundle needs; satisfied by the platform socket and test doubles. Handler events are `any`: the DOM and `ws` type them differently, and only `data` is read. */
226
217
  export interface SocketHandle {
227
218
  readonly readyState: number;
@@ -343,6 +334,5 @@ export declare const websocket: <const Name extends string>(config: {
343
334
  }>, Readonly<{
344
335
  send: (model: WebSocketModel, data: string) => Update.ReturnWithOutMessage<WebSocketModel, WebSocketMessage, never, SocketService>;
345
336
  }>>;
346
- //#endregion
347
337
  export {};
348
338
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/net/broadcast.ts","../../src/net/online.ts","../../src/net/sharedHost.ts","../../src/net/sse.ts","../../src/net/websocket.ts"],"mappings":";;;;;;qBAaa,4CAAgB;;;IAEf,MAAI,OAAA;;;;IAEG,SAAO,OAAA;;;YAEhB,0BAA0B,iBAAiB;;iBAGtC;EACf,YAAY;EACZ;EACA,aAAa;aAAkB;;;KAG5B,kBAAkB,iBAAiB;;;;;;qBAmB3B,oBAAiB;;AAGnB,SAAA,+BACR,OAAO,OAAO;;;;;;;qBAyBJ,gBAAa,cACZ,eACC,SACJ,+BACR,QAAQ;;;qBC3EE,aAAW,OAAA;mBAAX,OAAA;;YACD,qBAAqB,YAAY;qBAEhC,yCAAa;;IAAmC,QAAM,OAAA;;;YACvD,uBAAuB,cAAc;;;;;;qBAuBpC,QAAM,OAAA;;;;;;;;;iBC7BF;WACN,MAAM;WACN,QAAQ;;;YAIP,oBAAoB,YAAY,SAAS,SAAS,cAAc;;YAGhE,UAAU,iBAClB,oBAAoB,WACpB,QAAQ,oBAAoB;;iBAGf,kBAAkB;;;;;;WAMxB;aAAyB,MAAM;;;WAE/B,QAAQ,UAAU;;;iBAIZ,qBAAqB;;WAE3B,OAAO,SAAS,YAAY;;iBAGtB,WAAW;WACjB;;WAEA,QAAQ,OAAO,UAAU;;;;;;WAMzB,UAAU,SAAS,kBAAkB,aAAa,qBAAqB;;;;;;;cAQ5E,SAAU,SAAS,SAAO;WACrB;WACA,SAAS,OAAO,MAAM,SAAS;MACtC,WAAW;;;;;;;;;;;;qBA4IF;iBAAA;;;;;iBCxLI;WACN;EACT;EACA,UAAU;EACV,aAAa;EACb,WAAW;;qBAGA,UAAQ,OAAA;;;;;YAKT,kBAAkB,SAAS;qBAE1B,sCAAU;;;;;IAIT,MAAI,OAAA;;;;IAEN,SAAO,OAAA;;;YAEP,oBAAoB,WAAW;;;;;;;qBAQ9B,kCAAO;;;;;IAIT,OAAK,OAAA;;;YAEJ,iBAAiB,QAAQ;;;;;qBAMxB,SAAM,OAAW;;;;;;qBAQjB,YAAS,OAAW,UAAQ,oBAAoB;;iBAW5C;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;qBAIlB,QAAM,gBAAA,gBAAA,gBAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;;;;;;;qBAsBhD,YAAa,qBAAmB;WAClC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;;;;;;;;;;;;;;;;;;EA9BV,QAAA,gBAAgB,MACtB,UACA,YACA,OAAO,gBACP,gBACA,qBACM;;;;;iBChFO;WACN;EACT,KAAK;EACL;EACA,UAAU;EACV,aAAa;EACb,WAAW;EACX,WAAW;;qBAGA,gBAAc,OAAA;;;;;;;YAOf,wBAAwB,eAAe;qBAEtC,4CAAgB;;;;;IAIf,MAAI,OAAA;;;;IAEN,SAAO,OAAA;;;IACH,SAAO,OAAA;;;;IAEb,MAAI,OAAA;;;;;YAIF,0BAA0B,iBAAiB;;iBAGtC;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;;;;;;qBASlB,qCAAU;;;;;IAIZ,OAAK,OAAA;;;YAEJ,oBAAoB,WAAW;;;;;qBAM9B,SAAM,OAAW;;;;;;;qBASjB,SAAM,OAAW,gBAAc,oBAAoB;;qBAWnD,QAAM,gBAAA,gBAAA,UAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;;;;;;;;;qBAiDhD,kBAAmB,qBAAmB;WACxC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;WAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;EAzD9C,QAAA,gBAAgB,MACtB,gBACA,kBACA,OAAO,gBACP,UACA,qBACM;;EAKF,OACJ,OAAO,gBACP,iBACG,OAAO,qBAAqB,gBAAgB,yBAAyB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/net/broadcast.ts","../../src/net/online.ts","../../src/net/sharedHost.ts","../../src/net/sse.ts","../../src/net/websocket.ts"],"mappings":";;;;;qBAaa,4CAAgB;;;IAEf,MAAI,OAAA;;;;IAEG,SAAO,OAAA;;;YAEhB,0BAA0B,iBAAiB;;iBAGtC;EACf,YAAY;EACZ;EACA,aAAa;aAAkB;;;KAG5B,kBAAkB,iBAAiB;;;;;;qBAmB3B,oBAAiB;;AAGnB,SAAA,+BACR,OAAO,OAAO;;;;;;;qBAyBJ,gBAAa,cACZ,eACC,SACJ,+BACR,QAAQ;qBC3EE,aAAW,OAAA;;;YACZ,qBAAqB,YAAY;qBAEhC,yCAAa;;IAAmC,QAAM,OAAA;;;YACvD,uBAAuB,cAAc;;;;;;qBAuBpC,QAAM,OAAA;;;;;;;iBC7BF;WACN,MAAM;WACN,QAAQ;;;YAIP,oBAAoB,YAAY,SAAS,SAAS,cAAc;;YAGhE,UAAU,iBAClB,oBAAoB,WACpB,QAAQ,oBAAoB;;iBAGf,kBAAkB;;;;;;WAMxB;aAAyB,MAAM;;;WAE/B,QAAQ,UAAU;;;iBAIZ,qBAAqB;;WAE3B,OAAO,SAAS,YAAY;;iBAGtB,WAAW;WACjB;;WAEA,QAAQ,OAAO,UAAU;;;;;;WAMzB,UAAU,SAAS,kBAAkB,aAAa,qBAAqB;;;;;;;cAQ5E,SAAU,SAAS,SAAO;WACrB;WACA,SAAS,OAAO,MAAM,SAAS;MACtC,WAAW;;;;;;;;;;;;qBA4IF;;;;iBCxLI;WACN;EACT;EACA,UAAU;EACV,aAAa;EACb,WAAW;;qBAGA,UAAQ,OAAA;;;;;YAKT,kBAAkB,SAAS;qBAE1B,sCAAU;;;;;IAIT,MAAI,OAAA;;;;IAEN,SAAO,OAAA;;;YAEP,oBAAoB,WAAW;;;;;;;qBAQ9B,kCAAO;;;;;IAIT,OAAK,OAAA;;;YAEJ,iBAAiB,QAAQ;;;;;qBAMxB,SAAM,OAAW;;;;;;qBAQjB,YAAS,OAAW,UAAQ,oBAAoB;;iBAW5C;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;qBAIlB,QAAM,gBAAA,gBAAA,gBAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;;;;;;;qBAsBhD,YAAa,qBAAmB;WAClC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;;;;;;;;;;;;;;;;;;EA9BV,QAAA,gBAAgB,MACtB,UACA,YACA,OAAO,gBACP,gBACA,qBACM;;;iBChFO;WACN;EACT,KAAK;EACL;EACA,UAAU;EACV,aAAa;EACb,WAAW;EACX,WAAW;;qBAGA,gBAAc,OAAA;;;;;;;YAOf,wBAAwB,eAAe;qBAEtC,4CAAgB;;;;;IAIf,MAAI,OAAA;;;;IAEN,SAAO,OAAA;;;IACH,SAAO,OAAA;;;;IAEb,MAAI,OAAA;;;;;YAIF,0BAA0B,iBAAiB;;iBAGtC;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;;;;;;qBASlB,qCAAU;;;;;IAIZ,OAAK,OAAA;;;YAEJ,oBAAoB,WAAW;;;;;qBAM9B,SAAM,OAAW;;;;;;;qBASjB,SAAM,OAAW,gBAAc,oBAAoB;;qBAWnD,QAAM,gBAAA,gBAAA,UAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;;;;;;;;;qBAiDhD,kBAAmB,qBAAmB;WACxC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;WAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;EAzD9C,QAAA,gBAAgB,MACtB,gBACA,kBACA,OAAO,gBACP,UACA,qBACM;;EAKF,OACJ,OAAO,gBACP,iBACG,OAAO,qBAAqB,gBAAgB,yBAAyB"}
@@ -1,11 +1,11 @@
1
- import { i as unlessSame } from "../internal-DP-xLKbT.mjs";
1
+ import { i as unlessSame } from "../internal-C4gHodLc.mjs";
2
2
  import { Effect, Option, Queue, Schema, Stream } from "effect";
3
3
  import { defineMessageUnion } from "foldkit/message";
4
4
  import * as Subscription from "foldkit/subscription";
5
5
  import { Bundle } from "foldkit-bundle";
6
6
  import * as ManagedResource from "foldkit/managedResource";
7
7
  import { defineTaggedUnion } from "foldkit/schema";
8
- //#region src/net/broadcast.ts
8
+ // src/net/broadcast.ts
9
9
  /**
10
10
  * Cross-tab messaging as an entry plus a Command — not a bundle, because the
11
11
  * channel owns no state: the parent maps `Received` into its own Message and
@@ -73,8 +73,7 @@ const postBroadcast = (name, data, create) => ({
73
73
  onSuccess: () => Effect.succeed(BroadcastMessage.Posted())
74
74
  })
75
75
  });
76
- //#endregion
77
- //#region src/net/online.ts
76
+ // src/net/online.ts
78
77
  const OnlineModel = Schema.Struct({ online: Schema.Boolean });
79
78
  const OnlineMessage = defineMessageUnion({ Changed: { online: Schema.Boolean } });
80
79
  const presenceStream = () => {
@@ -93,8 +92,7 @@ const Online = Bundle.make("Online", {
93
92
  update: (model, message) => message.online === model.online ? { model } : { model: { online: message.online } },
94
93
  subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistentEntry(presenceStream()) }))
95
94
  });
96
- //#endregion
97
- //#region src/net/sharedHost.ts
95
+ // src/net/sharedHost.ts
98
96
  /**
99
97
  * A shared host: its `name`, which keeps two on one origin apart, and
100
98
  * the schema of what opens a conversation, which the host decodes before
@@ -214,8 +212,7 @@ const locks = () => Option.fromUndefinedOr(navigator.locks);
214
212
  * spoken over them: Sync's `portSocket` and Remote's `port` do that.
215
213
  */
216
214
  const SharedHost = { define };
217
- //#endregion
218
- //#region src/net/sse.ts
215
+ // src/net/sse.ts
219
216
  const SseModel = Schema.Struct({
220
217
  url: Schema.String,
221
218
  status: Schema.Literals([
@@ -341,8 +338,7 @@ const sse = (config) => {
341
338
  }) }))
342
339
  });
343
340
  };
344
- //#endregion
345
- //#region src/net/websocket.ts
341
+ // src/net/websocket.ts
346
342
  const WebSocketModel = Schema.Struct({
347
343
  url: Schema.String,
348
344
  status: Schema.Literals([
@@ -529,7 +525,6 @@ const websocket = (config) => {
529
525
  }) }
530
526
  });
531
527
  };
532
- //#endregion
533
528
  export { BroadcastMessage, Online, OnlineMessage, OnlineModel, SharedHost, Socket, SocketView, Source, SseMessage, SseModel, SseView, WebSocketMessage, WebSocketModel, broadcastMessages, isLive, isOpen, postBroadcast, sse, viewOf, viewOfSse, websocket };
534
529
 
535
530
  //# sourceMappingURL=index.mjs.map