@immediately-run/sdk 0.15.0 → 0.16.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.
Files changed (116) hide show
  1. package/README.md +27 -3
  2. package/dist/MDXProvider.cjs.map +1 -1
  3. package/dist/MDXProvider.d.cts +4 -0
  4. package/dist/MDXProvider.d.ts +4 -0
  5. package/dist/MDXProvider.js.map +1 -1
  6. package/dist/RoutingSpec.cjs.map +1 -1
  7. package/dist/RoutingSpec.d.cts +20 -3
  8. package/dist/RoutingSpec.d.ts +20 -3
  9. package/dist/auth.cjs.map +1 -1
  10. package/dist/auth.d.cts +2 -0
  11. package/dist/auth.d.ts +2 -0
  12. package/dist/auth.js.map +1 -1
  13. package/dist/boot.cjs +17 -7
  14. package/dist/boot.cjs.map +1 -1
  15. package/dist/boot.d.cts +28 -4
  16. package/dist/boot.d.ts +28 -4
  17. package/dist/boot.js +16 -7
  18. package/dist/boot.js.map +1 -1
  19. package/dist/components/Include.cjs.map +1 -1
  20. package/dist/components/Include.d.cts +7 -0
  21. package/dist/components/Include.d.ts +7 -0
  22. package/dist/components/Include.js.map +1 -1
  23. package/dist/components/MDXComponents.cjs.map +1 -1
  24. package/dist/components/MDXComponents.d.cts +6 -0
  25. package/dist/components/MDXComponents.d.ts +6 -0
  26. package/dist/components/MDXComponents.js.map +1 -1
  27. package/dist/components/Routes.cjs +59 -0
  28. package/dist/components/Routes.cjs.map +1 -0
  29. package/dist/components/Routes.d.cts +34 -0
  30. package/dist/components/Routes.d.ts +34 -0
  31. package/dist/components/Routes.js +34 -0
  32. package/dist/components/Routes.js.map +1 -0
  33. package/dist/contribute.cjs.map +1 -1
  34. package/dist/contribute.d.cts +2 -0
  35. package/dist/contribute.d.ts +2 -0
  36. package/dist/contribute.js.map +1 -1
  37. package/dist/diagnostics.cjs.map +1 -1
  38. package/dist/diagnostics.d.cts +3 -0
  39. package/dist/diagnostics.d.ts +3 -0
  40. package/dist/diagnostics.js.map +1 -1
  41. package/dist/formFactor.cjs.map +1 -1
  42. package/dist/formFactor.d.cts +2 -0
  43. package/dist/formFactor.d.ts +2 -0
  44. package/dist/formFactor.js.map +1 -1
  45. package/dist/hooks.cjs +27 -28
  46. package/dist/hooks.cjs.map +1 -1
  47. package/dist/hooks.d.cts +39 -4
  48. package/dist/hooks.d.ts +39 -4
  49. package/dist/hooks.js +27 -29
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.cjs +4 -0
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +6 -4
  54. package/dist/index.d.ts +6 -4
  55. package/dist/index.js +2 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/irMarkers.cjs.map +1 -1
  58. package/dist/irMarkers.d.cts +1 -0
  59. package/dist/irMarkers.d.ts +1 -0
  60. package/dist/irMarkers.js.map +1 -1
  61. package/dist/llm.cjs.map +1 -1
  62. package/dist/llm.d.cts +5 -0
  63. package/dist/llm.d.ts +5 -0
  64. package/dist/llm.js.map +1 -1
  65. package/dist/loading.cjs +186 -0
  66. package/dist/loading.cjs.map +1 -0
  67. package/dist/loading.d.cts +48 -0
  68. package/dist/loading.d.ts +48 -0
  69. package/dist/loading.js +162 -0
  70. package/dist/loading.js.map +1 -0
  71. package/dist/mounts.cjs.map +1 -1
  72. package/dist/mounts.d.cts +3 -1
  73. package/dist/mounts.d.ts +3 -1
  74. package/dist/mounts.js.map +1 -1
  75. package/dist/netFetch.cjs.map +1 -1
  76. package/dist/netFetch.d.cts +2 -0
  77. package/dist/netFetch.d.ts +2 -0
  78. package/dist/netFetch.js.map +1 -1
  79. package/dist/onFsChange.cjs.map +1 -1
  80. package/dist/onFsChange.d.cts +1 -0
  81. package/dist/onFsChange.d.ts +1 -0
  82. package/dist/onFsChange.js.map +1 -1
  83. package/dist/protocolStream.cjs.map +1 -1
  84. package/dist/protocolStream.d.cts +3 -0
  85. package/dist/protocolStream.d.ts +3 -0
  86. package/dist/protocolStream.js.map +1 -1
  87. package/dist/ready.cjs.map +1 -1
  88. package/dist/ready.d.cts +7 -0
  89. package/dist/ready.d.ts +7 -0
  90. package/dist/ready.js.map +1 -1
  91. package/dist/routeMatch.cjs +72 -0
  92. package/dist/routeMatch.cjs.map +1 -0
  93. package/dist/routeMatch.d.cts +19 -0
  94. package/dist/routeMatch.d.ts +19 -0
  95. package/dist/routeMatch.js +46 -0
  96. package/dist/routeMatch.js.map +1 -0
  97. package/dist/routing.cjs +35 -14
  98. package/dist/routing.cjs.map +1 -1
  99. package/dist/routing.d.cts +33 -4
  100. package/dist/routing.d.ts +33 -4
  101. package/dist/routing.js +32 -14
  102. package/dist/routing.js.map +1 -1
  103. package/dist/runtime.cjs.map +1 -1
  104. package/dist/runtime.d.cts +1 -0
  105. package/dist/runtime.d.ts +1 -0
  106. package/dist/runtime.js.map +1 -1
  107. package/dist/sandboxTypes.cjs.map +1 -1
  108. package/dist/sandboxTypes.d.cts +30 -7
  109. package/dist/sandboxTypes.d.ts +30 -7
  110. package/dist/version.cjs +1 -1
  111. package/dist/version.cjs.map +1 -1
  112. package/dist/version.d.cts +1 -1
  113. package/dist/version.d.ts +1 -1
  114. package/dist/version.js +1 -1
  115. package/dist/version.js.map +1 -1
  116. package/package.json +6 -2
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var loading_exports = {};
20
+ __export(loading_exports, {
21
+ LOADING_TIMINGS: () => LOADING_TIMINGS,
22
+ LoadingRegion: () => LoadingRegion,
23
+ Skeleton: () => Skeleton,
24
+ SkeletonRow: () => SkeletonRow,
25
+ Spinner: () => Spinner
26
+ });
27
+ module.exports = __toCommonJS(loading_exports);
28
+ var import_jsx_runtime = require("react/jsx-runtime");
29
+ var import_react = require("react");
30
+ const LOADING_TIMINGS = {
31
+ /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */
32
+ spinThresholdMs: 150,
33
+ /** The reveal cross-fade duration the host uses. */
34
+ fadeMs: 150,
35
+ /** One slow shimmer sweep across a placeholder. */
36
+ shimmerMs: 1900
37
+ };
38
+ const STYLE_ID = "ir-sdk-loading-styles";
39
+ const STYLE_TEXT = `
40
+ @keyframes ir-sdk-sweep{0%{transform:translateX(-130%)}60%,100%{transform:translateX(130%)}}
41
+ @keyframes ir-sdk-spin{to{transform:rotate(360deg)}}
42
+ .ir-sdk-shim{position:relative;overflow:hidden}
43
+ .ir-sdk-shim::after{content:"";position:absolute;inset:0;background:linear-gradient(100deg,transparent 28%,rgba(127,127,127,.18) 50%,transparent 72%);transform:translateX(-130%);animation:ir-sdk-sweep ${LOADING_TIMINGS.shimmerMs}ms ease-in-out infinite}
44
+ .ir-sdk-spin{animation:ir-sdk-spin .8s linear infinite}
45
+ @media (prefers-reduced-motion:reduce){.ir-sdk-shim::after{display:none}.ir-sdk-spin{animation:none}}
46
+ `;
47
+ function ensureLoadingStyles() {
48
+ if (typeof document === "undefined") return;
49
+ if (document.getElementById(STYLE_ID)) return;
50
+ const el = document.createElement("style");
51
+ el.id = STYLE_ID;
52
+ el.textContent = STYLE_TEXT;
53
+ document.head.appendChild(el);
54
+ }
55
+ function useLoadingStyles() {
56
+ (0, import_react.useLayoutEffect)(() => {
57
+ ensureLoadingStyles();
58
+ }, []);
59
+ }
60
+ const PLACEHOLDER = {
61
+ background: "rgba(127,127,127,0.20)",
62
+ borderRadius: 6
63
+ };
64
+ function SkeletonRow({
65
+ width = "100%",
66
+ height = 12,
67
+ style
68
+ }) {
69
+ useLoadingStyles();
70
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
71
+ "div",
72
+ {
73
+ className: "ir-sdk-shim",
74
+ "aria-hidden": "true",
75
+ style: { ...PLACEHOLDER, width, height, ...style }
76
+ }
77
+ );
78
+ }
79
+ function rows(specs) {
80
+ return specs.map(({ key, ...s }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ir-sdk-shim", "aria-hidden": "true", style: { ...PLACEHOLDER, ...s } }, key));
81
+ }
82
+ function Skeleton({
83
+ archetype = "generic",
84
+ style
85
+ }) {
86
+ useLoadingStyles();
87
+ const wrap = {
88
+ display: "flex",
89
+ flexDirection: "column",
90
+ gap: 10,
91
+ padding: 12,
92
+ width: "100%",
93
+ boxSizing: "border-box",
94
+ ...style
95
+ };
96
+ let body;
97
+ switch (archetype) {
98
+ case "panel.list":
99
+ body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14 })));
100
+ break;
101
+ case "panel.tree":
102
+ body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14, marginLeft: key % 3 * 16 })));
103
+ break;
104
+ case "panel.editor":
105
+ body = rows([62, 88, 73, 41, 80].map((w, key) => ({ key, height: 11, width: `${w}%` })));
106
+ break;
107
+ case "panel.conversation":
108
+ body = rows(
109
+ [0, 1, 2, 3].map((key) => ({
110
+ key,
111
+ height: 40,
112
+ width: "70%",
113
+ borderRadius: 12,
114
+ alignSelf: key % 2 ? "flex-end" : "flex-start"
115
+ }))
116
+ );
117
+ break;
118
+ case "generic":
119
+ default:
120
+ body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ir-sdk-shim", "aria-hidden": "true", style: { ...PLACEHOLDER, height: 16, width: "45%", borderRadius: 4 } }),
122
+ rows([0, 1].map((key) => ({ key, height: 56, borderRadius: 8 })))
123
+ ] });
124
+ }
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": "true", style: wrap, children: body });
126
+ }
127
+ function useDelayedFlag(ms) {
128
+ const [on, setOn] = (0, import_react.useState)(false);
129
+ (0, import_react.useEffect)(() => {
130
+ const t = setTimeout(() => setOn(true), ms);
131
+ return () => clearTimeout(t);
132
+ }, [ms]);
133
+ return on;
134
+ }
135
+ function Spinner({
136
+ size = 18,
137
+ thresholdMs = LOADING_TIMINGS.spinThresholdMs,
138
+ label = "Loading"
139
+ }) {
140
+ useLoadingStyles();
141
+ const show = useDelayedFlag(thresholdMs);
142
+ if (!show) return null;
143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
144
+ "span",
145
+ {
146
+ className: "ir-sdk-spin",
147
+ role: "status",
148
+ "aria-label": label,
149
+ style: {
150
+ display: "inline-block",
151
+ width: size,
152
+ height: size,
153
+ border: "2px solid rgba(127,127,127,0.3)",
154
+ borderTopColor: "currentColor",
155
+ borderRadius: "50%",
156
+ boxSizing: "border-box"
157
+ }
158
+ }
159
+ );
160
+ }
161
+ function LoadingRegion({
162
+ loading,
163
+ fallback,
164
+ label = "Loading",
165
+ children
166
+ }) {
167
+ useLoadingStyles();
168
+ if (!loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
169
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
170
+ "div",
171
+ {
172
+ "aria-busy": "true",
173
+ style: { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", minHeight: 48 },
174
+ children: fallback ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { label })
175
+ }
176
+ );
177
+ }
178
+ // Annotate the CommonJS export names for ESM import in node:
179
+ 0 && (module.exports = {
180
+ LOADING_TIMINGS,
181
+ LoadingRegion,
182
+ Skeleton,
183
+ SkeletonRow,
184
+ Spinner
185
+ });
186
+ //# sourceMappingURL=loading.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loading.tsx"],"sourcesContent":["// In-app loading primitives (LOADING_UX_SPEC §9, Surface E; design brief 19).\n//\n// Opt-in, presentational components an app author drops in for IN-APP waits (a\n// React.lazy chunk, a slow fetch, a pending action) so they match the platform's\n// loading language for free. Per product_values §3 this is convenience — a plain\n// app that ignores it still works.\n//\n// TRUST BOUNDARY (read brief 19): these render INSIDE the app's iframe, under the\n// app's principal — presentational only, NO capability, NO host round-trip. They\n// share the token VOCABULARY (radii, the shimmer sweep, soft placeholder fills)\n// with the host skeletons, but they are deliberately NOT the host's trusted chrome:\n// no reserved landmark, no wordmark, no \"platform is loading\" framing. They use\n// ordinary app a11y (`aria-hidden` for decorative skeletons, `role=\"status\"` for an\n// indeterminate indicator) — never the host's reserved landmark/accessible name\n// (that is the trusted-path control, reserved to the host). An in-app loader that\n// mimicked host chrome would be a spoofing vector.\n//\n// Self-contained: the SDK ships no CSS pipeline, so the keyframes are injected once\n// into the document and everything else is inline styles — zero config for the author.\nimport {\n useEffect,\n useLayoutEffect,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\n/** Loading timing constants, matching the host (LOADING_UX_SPEC §3, the 2026-06-22\n * design bundle). Exported so authors who hand-roll still match the platform. */\nexport const LOADING_TIMINGS = {\n /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */\n spinThresholdMs: 150,\n /** The reveal cross-fade duration the host uses. */\n fadeMs: 150,\n /** One slow shimmer sweep across a placeholder. */\n shimmerMs: 1900,\n} as const;\n\n/** The in-app skeleton archetypes (the same shapes as the host §4.1 catalog). */\nexport type SkeletonArchetype =\n | \"panel.list\"\n | \"panel.tree\"\n | \"panel.editor\"\n | \"panel.conversation\"\n | \"generic\";\n\nconst STYLE_ID = \"ir-sdk-loading-styles\";\nconst STYLE_TEXT = `\n@keyframes ir-sdk-sweep{0%{transform:translateX(-130%)}60%,100%{transform:translateX(130%)}}\n@keyframes ir-sdk-spin{to{transform:rotate(360deg)}}\n.ir-sdk-shim{position:relative;overflow:hidden}\n.ir-sdk-shim::after{content:\"\";position:absolute;inset:0;background:linear-gradient(100deg,transparent 28%,rgba(127,127,127,.18) 50%,transparent 72%);transform:translateX(-130%);animation:ir-sdk-sweep ${LOADING_TIMINGS.shimmerMs}ms ease-in-out infinite}\n.ir-sdk-spin{animation:ir-sdk-spin .8s linear infinite}\n@media (prefers-reduced-motion:reduce){.ir-sdk-shim::after{display:none}.ir-sdk-spin{animation:none}}\n`;\n\n/** Inject the shimmer/spin keyframes once (idempotent, browser-only). */\nfunction ensureLoadingStyles(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = STYLE_TEXT;\n document.head.appendChild(el);\n}\n\n/** Ensure the loading keyframes exist before the component paints. */\nfunction useLoadingStyles(): void {\n useLayoutEffect(() => {\n ensureLoadingStyles();\n }, []);\n}\n\n// A soft, low-contrast placeholder fill that reads on a light OR dark app surface.\nconst PLACEHOLDER: CSSProperties = {\n background: \"rgba(127,127,127,0.20)\",\n borderRadius: 6,\n};\n\n/** A single placeholder bar — compose these into a custom skeleton shape. */\nexport function SkeletonRow({\n width = \"100%\",\n height = 12,\n style,\n}: {\n width?: number | string;\n height?: number | string;\n style?: CSSProperties;\n}) {\n useLoadingStyles();\n return (\n <div\n className=\"ir-sdk-shim\"\n aria-hidden=\"true\"\n style={{ ...PLACEHOLDER, width, height, ...style }}\n />\n );\n}\n\nfunction rows(specs: Array<CSSProperties & { key: number }>): ReactNode {\n return specs.map(({ key, ...s }) => (\n <div key={key} className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, ...s }} />\n ));\n}\n\n/** A shaped, in-app skeleton matching the host archetypes — for an app's own lazy\n * region (e.g. `<Suspense fallback={<Skeleton archetype=\"panel.list\" />}>`).\n * Decorative (`aria-hidden`); pair it with `aria-busy` on the region it stands in. */\nexport function Skeleton({\n archetype = \"generic\",\n style,\n}: {\n archetype?: SkeletonArchetype;\n style?: CSSProperties;\n}) {\n useLoadingStyles();\n const wrap: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 10,\n padding: 12,\n width: \"100%\",\n boxSizing: \"border-box\",\n ...style,\n };\n let body: ReactNode;\n switch (archetype) {\n case \"panel.list\":\n body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14 })));\n break;\n case \"panel.tree\":\n body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14, marginLeft: (key % 3) * 16 })));\n break;\n case \"panel.editor\":\n body = rows([62, 88, 73, 41, 80].map((w, key) => ({ key, height: 11, width: `${w}%` })));\n break;\n case \"panel.conversation\":\n body = rows(\n [0, 1, 2, 3].map((key) => ({\n key,\n height: 40,\n width: \"70%\",\n borderRadius: 12,\n alignSelf: key % 2 ? \"flex-end\" : \"flex-start\",\n })),\n );\n break;\n case \"generic\":\n default:\n body = (\n <>\n <div className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, height: 16, width: \"45%\", borderRadius: 4 }} />\n {rows([0, 1].map((key) => ({ key, height: 56, borderRadius: 8 })))}\n </>\n );\n }\n return (\n <div aria-hidden=\"true\" style={wrap}>\n {body}\n </div>\n );\n}\n\n/** Become true after `ms`, so a fast wait never flashes an indicator (§6.2 floor). */\nfunction useDelayedFlag(ms: number): boolean {\n const [on, setOn] = useState(false);\n useEffect(() => {\n const t = setTimeout(() => setOn(true), ms);\n return () => clearTimeout(t);\n }, [ms]);\n return on;\n}\n\n/** An indeterminate spinner for waits where a shaped skeleton doesn't fit (a pending\n * button, a small inline fetch). Wired to the host's ~150 ms-before-spin rule: it\n * renders nothing until the threshold, so a fast wait shows no flash. Reduced motion\n * stills the rotation (the ring stays as a static indicator). In-app a11y only. */\nexport function Spinner({\n size = 18,\n thresholdMs = LOADING_TIMINGS.spinThresholdMs,\n label = \"Loading\",\n}: {\n size?: number;\n thresholdMs?: number;\n label?: string;\n}) {\n useLoadingStyles();\n const show = useDelayedFlag(thresholdMs);\n if (!show) return null;\n return (\n <span\n className=\"ir-sdk-spin\"\n role=\"status\"\n aria-label={label}\n style={{\n display: \"inline-block\",\n width: size,\n height: size,\n border: \"2px solid rgba(127,127,127,0.3)\",\n borderTopColor: \"currentColor\",\n borderRadius: \"50%\",\n boxSizing: \"border-box\",\n }}\n />\n );\n}\n\n/** Wrap an in-app region whose content is still loading: shows a centered spinner\n * (past the 150 ms floor) with `aria-busy`, then reveals `children`. For a shaped\n * wait, pass a `<Skeleton>` as `fallback` instead. App a11y only — not host chrome. */\nexport function LoadingRegion({\n loading,\n fallback,\n label = \"Loading\",\n children,\n}: {\n loading: boolean;\n fallback?: ReactNode;\n label?: string;\n children?: ReactNode;\n}) {\n useLoadingStyles();\n if (!loading) return <>{children}</>;\n return (\n <div\n aria-busy=\"true\"\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"center\", width: \"100%\", height: \"100%\", minHeight: 48 }}\n >\n {fallback ?? <Spinner label={label} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ /** Loading timing constants, matching the host (LOADING_UX_SPEC §3, the 2026-06-22
5
+ * design bundle). Exported so authors who hand-roll still match the platform. */
6
+ declare const LOADING_TIMINGS: {
7
+ /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */
8
+ readonly spinThresholdMs: 150;
9
+ /** The reveal cross-fade duration the host uses. */
10
+ readonly fadeMs: 150;
11
+ /** One slow shimmer sweep across a placeholder. */
12
+ readonly shimmerMs: 1900;
13
+ };
14
+ /** The in-app skeleton archetypes (the same shapes as the host §4.1 catalog). */
15
+ type SkeletonArchetype = "panel.list" | "panel.tree" | "panel.editor" | "panel.conversation" | "generic";
16
+ /** A single placeholder bar — compose these into a custom skeleton shape. */
17
+ declare function SkeletonRow({ width, height, style, }: {
18
+ width?: number | string;
19
+ height?: number | string;
20
+ style?: CSSProperties;
21
+ }): react_jsx_runtime.JSX.Element;
22
+ /** A shaped, in-app skeleton matching the host archetypes — for an app's own lazy
23
+ * region (e.g. `<Suspense fallback={<Skeleton archetype="panel.list" />}>`).
24
+ * Decorative (`aria-hidden`); pair it with `aria-busy` on the region it stands in. */
25
+ declare function Skeleton({ archetype, style, }: {
26
+ archetype?: SkeletonArchetype;
27
+ style?: CSSProperties;
28
+ }): react_jsx_runtime.JSX.Element;
29
+ /** An indeterminate spinner for waits where a shaped skeleton doesn't fit (a pending
30
+ * button, a small inline fetch). Wired to the host's ~150 ms-before-spin rule: it
31
+ * renders nothing until the threshold, so a fast wait shows no flash. Reduced motion
32
+ * stills the rotation (the ring stays as a static indicator). In-app a11y only. */
33
+ declare function Spinner({ size, thresholdMs, label, }: {
34
+ size?: number;
35
+ thresholdMs?: number;
36
+ label?: string;
37
+ }): react_jsx_runtime.JSX.Element | null;
38
+ /** Wrap an in-app region whose content is still loading: shows a centered spinner
39
+ * (past the 150 ms floor) with `aria-busy`, then reveals `children`. For a shaped
40
+ * wait, pass a `<Skeleton>` as `fallback` instead. App a11y only — not host chrome. */
41
+ declare function LoadingRegion({ loading, fallback, label, children, }: {
42
+ loading: boolean;
43
+ fallback?: ReactNode;
44
+ label?: string;
45
+ children?: ReactNode;
46
+ }): react_jsx_runtime.JSX.Element;
47
+
48
+ export { LOADING_TIMINGS, LoadingRegion, Skeleton, type SkeletonArchetype, SkeletonRow, Spinner };
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ /** Loading timing constants, matching the host (LOADING_UX_SPEC §3, the 2026-06-22
5
+ * design bundle). Exported so authors who hand-roll still match the platform. */
6
+ declare const LOADING_TIMINGS: {
7
+ /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */
8
+ readonly spinThresholdMs: 150;
9
+ /** The reveal cross-fade duration the host uses. */
10
+ readonly fadeMs: 150;
11
+ /** One slow shimmer sweep across a placeholder. */
12
+ readonly shimmerMs: 1900;
13
+ };
14
+ /** The in-app skeleton archetypes (the same shapes as the host §4.1 catalog). */
15
+ type SkeletonArchetype = "panel.list" | "panel.tree" | "panel.editor" | "panel.conversation" | "generic";
16
+ /** A single placeholder bar — compose these into a custom skeleton shape. */
17
+ declare function SkeletonRow({ width, height, style, }: {
18
+ width?: number | string;
19
+ height?: number | string;
20
+ style?: CSSProperties;
21
+ }): react_jsx_runtime.JSX.Element;
22
+ /** A shaped, in-app skeleton matching the host archetypes — for an app's own lazy
23
+ * region (e.g. `<Suspense fallback={<Skeleton archetype="panel.list" />}>`).
24
+ * Decorative (`aria-hidden`); pair it with `aria-busy` on the region it stands in. */
25
+ declare function Skeleton({ archetype, style, }: {
26
+ archetype?: SkeletonArchetype;
27
+ style?: CSSProperties;
28
+ }): react_jsx_runtime.JSX.Element;
29
+ /** An indeterminate spinner for waits where a shaped skeleton doesn't fit (a pending
30
+ * button, a small inline fetch). Wired to the host's ~150 ms-before-spin rule: it
31
+ * renders nothing until the threshold, so a fast wait shows no flash. Reduced motion
32
+ * stills the rotation (the ring stays as a static indicator). In-app a11y only. */
33
+ declare function Spinner({ size, thresholdMs, label, }: {
34
+ size?: number;
35
+ thresholdMs?: number;
36
+ label?: string;
37
+ }): react_jsx_runtime.JSX.Element | null;
38
+ /** Wrap an in-app region whose content is still loading: shows a centered spinner
39
+ * (past the 150 ms floor) with `aria-busy`, then reveals `children`. For a shaped
40
+ * wait, pass a `<Skeleton>` as `fallback` instead. App a11y only — not host chrome. */
41
+ declare function LoadingRegion({ loading, fallback, label, children, }: {
42
+ loading: boolean;
43
+ fallback?: ReactNode;
44
+ label?: string;
45
+ children?: ReactNode;
46
+ }): react_jsx_runtime.JSX.Element;
47
+
48
+ export { LOADING_TIMINGS, LoadingRegion, Skeleton, type SkeletonArchetype, SkeletonRow, Spinner };
@@ -0,0 +1,162 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ useEffect,
4
+ useLayoutEffect,
5
+ useState
6
+ } from "react";
7
+ const LOADING_TIMINGS = {
8
+ /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */
9
+ spinThresholdMs: 150,
10
+ /** The reveal cross-fade duration the host uses. */
11
+ fadeMs: 150,
12
+ /** One slow shimmer sweep across a placeholder. */
13
+ shimmerMs: 1900
14
+ };
15
+ const STYLE_ID = "ir-sdk-loading-styles";
16
+ const STYLE_TEXT = `
17
+ @keyframes ir-sdk-sweep{0%{transform:translateX(-130%)}60%,100%{transform:translateX(130%)}}
18
+ @keyframes ir-sdk-spin{to{transform:rotate(360deg)}}
19
+ .ir-sdk-shim{position:relative;overflow:hidden}
20
+ .ir-sdk-shim::after{content:"";position:absolute;inset:0;background:linear-gradient(100deg,transparent 28%,rgba(127,127,127,.18) 50%,transparent 72%);transform:translateX(-130%);animation:ir-sdk-sweep ${LOADING_TIMINGS.shimmerMs}ms ease-in-out infinite}
21
+ .ir-sdk-spin{animation:ir-sdk-spin .8s linear infinite}
22
+ @media (prefers-reduced-motion:reduce){.ir-sdk-shim::after{display:none}.ir-sdk-spin{animation:none}}
23
+ `;
24
+ function ensureLoadingStyles() {
25
+ if (typeof document === "undefined") return;
26
+ if (document.getElementById(STYLE_ID)) return;
27
+ const el = document.createElement("style");
28
+ el.id = STYLE_ID;
29
+ el.textContent = STYLE_TEXT;
30
+ document.head.appendChild(el);
31
+ }
32
+ function useLoadingStyles() {
33
+ useLayoutEffect(() => {
34
+ ensureLoadingStyles();
35
+ }, []);
36
+ }
37
+ const PLACEHOLDER = {
38
+ background: "rgba(127,127,127,0.20)",
39
+ borderRadius: 6
40
+ };
41
+ function SkeletonRow({
42
+ width = "100%",
43
+ height = 12,
44
+ style
45
+ }) {
46
+ useLoadingStyles();
47
+ return /* @__PURE__ */ jsx(
48
+ "div",
49
+ {
50
+ className: "ir-sdk-shim",
51
+ "aria-hidden": "true",
52
+ style: { ...PLACEHOLDER, width, height, ...style }
53
+ }
54
+ );
55
+ }
56
+ function rows(specs) {
57
+ return specs.map(({ key, ...s }) => /* @__PURE__ */ jsx("div", { className: "ir-sdk-shim", "aria-hidden": "true", style: { ...PLACEHOLDER, ...s } }, key));
58
+ }
59
+ function Skeleton({
60
+ archetype = "generic",
61
+ style
62
+ }) {
63
+ useLoadingStyles();
64
+ const wrap = {
65
+ display: "flex",
66
+ flexDirection: "column",
67
+ gap: 10,
68
+ padding: 12,
69
+ width: "100%",
70
+ boxSizing: "border-box",
71
+ ...style
72
+ };
73
+ let body;
74
+ switch (archetype) {
75
+ case "panel.list":
76
+ body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14 })));
77
+ break;
78
+ case "panel.tree":
79
+ body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14, marginLeft: key % 3 * 16 })));
80
+ break;
81
+ case "panel.editor":
82
+ body = rows([62, 88, 73, 41, 80].map((w, key) => ({ key, height: 11, width: `${w}%` })));
83
+ break;
84
+ case "panel.conversation":
85
+ body = rows(
86
+ [0, 1, 2, 3].map((key) => ({
87
+ key,
88
+ height: 40,
89
+ width: "70%",
90
+ borderRadius: 12,
91
+ alignSelf: key % 2 ? "flex-end" : "flex-start"
92
+ }))
93
+ );
94
+ break;
95
+ case "generic":
96
+ default:
97
+ body = /* @__PURE__ */ jsxs(Fragment, { children: [
98
+ /* @__PURE__ */ jsx("div", { className: "ir-sdk-shim", "aria-hidden": "true", style: { ...PLACEHOLDER, height: 16, width: "45%", borderRadius: 4 } }),
99
+ rows([0, 1].map((key) => ({ key, height: 56, borderRadius: 8 })))
100
+ ] });
101
+ }
102
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: wrap, children: body });
103
+ }
104
+ function useDelayedFlag(ms) {
105
+ const [on, setOn] = useState(false);
106
+ useEffect(() => {
107
+ const t = setTimeout(() => setOn(true), ms);
108
+ return () => clearTimeout(t);
109
+ }, [ms]);
110
+ return on;
111
+ }
112
+ function Spinner({
113
+ size = 18,
114
+ thresholdMs = LOADING_TIMINGS.spinThresholdMs,
115
+ label = "Loading"
116
+ }) {
117
+ useLoadingStyles();
118
+ const show = useDelayedFlag(thresholdMs);
119
+ if (!show) return null;
120
+ return /* @__PURE__ */ jsx(
121
+ "span",
122
+ {
123
+ className: "ir-sdk-spin",
124
+ role: "status",
125
+ "aria-label": label,
126
+ style: {
127
+ display: "inline-block",
128
+ width: size,
129
+ height: size,
130
+ border: "2px solid rgba(127,127,127,0.3)",
131
+ borderTopColor: "currentColor",
132
+ borderRadius: "50%",
133
+ boxSizing: "border-box"
134
+ }
135
+ }
136
+ );
137
+ }
138
+ function LoadingRegion({
139
+ loading,
140
+ fallback,
141
+ label = "Loading",
142
+ children
143
+ }) {
144
+ useLoadingStyles();
145
+ if (!loading) return /* @__PURE__ */ jsx(Fragment, { children });
146
+ return /* @__PURE__ */ jsx(
147
+ "div",
148
+ {
149
+ "aria-busy": "true",
150
+ style: { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", minHeight: 48 },
151
+ children: fallback ?? /* @__PURE__ */ jsx(Spinner, { label })
152
+ }
153
+ );
154
+ }
155
+ export {
156
+ LOADING_TIMINGS,
157
+ LoadingRegion,
158
+ Skeleton,
159
+ SkeletonRow,
160
+ Spinner
161
+ };
162
+ //# sourceMappingURL=loading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loading.tsx"],"sourcesContent":["// In-app loading primitives (LOADING_UX_SPEC §9, Surface E; design brief 19).\n//\n// Opt-in, presentational components an app author drops in for IN-APP waits (a\n// React.lazy chunk, a slow fetch, a pending action) so they match the platform's\n// loading language for free. Per product_values §3 this is convenience — a plain\n// app that ignores it still works.\n//\n// TRUST BOUNDARY (read brief 19): these render INSIDE the app's iframe, under the\n// app's principal — presentational only, NO capability, NO host round-trip. They\n// share the token VOCABULARY (radii, the shimmer sweep, soft placeholder fills)\n// with the host skeletons, but they are deliberately NOT the host's trusted chrome:\n// no reserved landmark, no wordmark, no \"platform is loading\" framing. They use\n// ordinary app a11y (`aria-hidden` for decorative skeletons, `role=\"status\"` for an\n// indeterminate indicator) — never the host's reserved landmark/accessible name\n// (that is the trusted-path control, reserved to the host). An in-app loader that\n// mimicked host chrome would be a spoofing vector.\n//\n// Self-contained: the SDK ships no CSS pipeline, so the keyframes are injected once\n// into the document and everything else is inline styles — zero config for the author.\nimport {\n useEffect,\n useLayoutEffect,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\n/** Loading timing constants, matching the host (LOADING_UX_SPEC §3, the 2026-06-22\n * design bundle). Exported so authors who hand-roll still match the platform. */\nexport const LOADING_TIMINGS = {\n /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */\n spinThresholdMs: 150,\n /** The reveal cross-fade duration the host uses. */\n fadeMs: 150,\n /** One slow shimmer sweep across a placeholder. */\n shimmerMs: 1900,\n} as const;\n\n/** The in-app skeleton archetypes (the same shapes as the host §4.1 catalog). */\nexport type SkeletonArchetype =\n | \"panel.list\"\n | \"panel.tree\"\n | \"panel.editor\"\n | \"panel.conversation\"\n | \"generic\";\n\nconst STYLE_ID = \"ir-sdk-loading-styles\";\nconst STYLE_TEXT = `\n@keyframes ir-sdk-sweep{0%{transform:translateX(-130%)}60%,100%{transform:translateX(130%)}}\n@keyframes ir-sdk-spin{to{transform:rotate(360deg)}}\n.ir-sdk-shim{position:relative;overflow:hidden}\n.ir-sdk-shim::after{content:\"\";position:absolute;inset:0;background:linear-gradient(100deg,transparent 28%,rgba(127,127,127,.18) 50%,transparent 72%);transform:translateX(-130%);animation:ir-sdk-sweep ${LOADING_TIMINGS.shimmerMs}ms ease-in-out infinite}\n.ir-sdk-spin{animation:ir-sdk-spin .8s linear infinite}\n@media (prefers-reduced-motion:reduce){.ir-sdk-shim::after{display:none}.ir-sdk-spin{animation:none}}\n`;\n\n/** Inject the shimmer/spin keyframes once (idempotent, browser-only). */\nfunction ensureLoadingStyles(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = STYLE_TEXT;\n document.head.appendChild(el);\n}\n\n/** Ensure the loading keyframes exist before the component paints. */\nfunction useLoadingStyles(): void {\n useLayoutEffect(() => {\n ensureLoadingStyles();\n }, []);\n}\n\n// A soft, low-contrast placeholder fill that reads on a light OR dark app surface.\nconst PLACEHOLDER: CSSProperties = {\n background: \"rgba(127,127,127,0.20)\",\n borderRadius: 6,\n};\n\n/** A single placeholder bar — compose these into a custom skeleton shape. */\nexport function SkeletonRow({\n width = \"100%\",\n height = 12,\n style,\n}: {\n width?: number | string;\n height?: number | string;\n style?: CSSProperties;\n}) {\n useLoadingStyles();\n return (\n <div\n className=\"ir-sdk-shim\"\n aria-hidden=\"true\"\n style={{ ...PLACEHOLDER, width, height, ...style }}\n />\n );\n}\n\nfunction rows(specs: Array<CSSProperties & { key: number }>): ReactNode {\n return specs.map(({ key, ...s }) => (\n <div key={key} className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, ...s }} />\n ));\n}\n\n/** A shaped, in-app skeleton matching the host archetypes — for an app's own lazy\n * region (e.g. `<Suspense fallback={<Skeleton archetype=\"panel.list\" />}>`).\n * Decorative (`aria-hidden`); pair it with `aria-busy` on the region it stands in. */\nexport function Skeleton({\n archetype = \"generic\",\n style,\n}: {\n archetype?: SkeletonArchetype;\n style?: CSSProperties;\n}) {\n useLoadingStyles();\n const wrap: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 10,\n padding: 12,\n width: \"100%\",\n boxSizing: \"border-box\",\n ...style,\n };\n let body: ReactNode;\n switch (archetype) {\n case \"panel.list\":\n body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14 })));\n break;\n case \"panel.tree\":\n body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14, marginLeft: (key % 3) * 16 })));\n break;\n case \"panel.editor\":\n body = rows([62, 88, 73, 41, 80].map((w, key) => ({ key, height: 11, width: `${w}%` })));\n break;\n case \"panel.conversation\":\n body = rows(\n [0, 1, 2, 3].map((key) => ({\n key,\n height: 40,\n width: \"70%\",\n borderRadius: 12,\n alignSelf: key % 2 ? \"flex-end\" : \"flex-start\",\n })),\n );\n break;\n case \"generic\":\n default:\n body = (\n <>\n <div className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, height: 16, width: \"45%\", borderRadius: 4 }} />\n {rows([0, 1].map((key) => ({ key, height: 56, borderRadius: 8 })))}\n </>\n );\n }\n return (\n <div aria-hidden=\"true\" style={wrap}>\n {body}\n </div>\n );\n}\n\n/** Become true after `ms`, so a fast wait never flashes an indicator (§6.2 floor). */\nfunction useDelayedFlag(ms: number): boolean {\n const [on, setOn] = useState(false);\n useEffect(() => {\n const t = setTimeout(() => setOn(true), ms);\n return () => clearTimeout(t);\n }, [ms]);\n return on;\n}\n\n/** An indeterminate spinner for waits where a shaped skeleton doesn't fit (a pending\n * button, a small inline fetch). Wired to the host's ~150 ms-before-spin rule: it\n * renders nothing until the threshold, so a fast wait shows no flash. Reduced motion\n * stills the rotation (the ring stays as a static indicator). In-app a11y only. */\nexport function Spinner({\n size = 18,\n thresholdMs = LOADING_TIMINGS.spinThresholdMs,\n label = \"Loading\",\n}: {\n size?: number;\n thresholdMs?: number;\n label?: string;\n}) {\n useLoadingStyles();\n const show = useDelayedFlag(thresholdMs);\n if (!show) return null;\n return (\n <span\n className=\"ir-sdk-spin\"\n role=\"status\"\n aria-label={label}\n style={{\n display: \"inline-block\",\n width: size,\n height: size,\n border: \"2px solid rgba(127,127,127,0.3)\",\n borderTopColor: \"currentColor\",\n borderRadius: \"50%\",\n boxSizing: \"border-box\",\n }}\n />\n );\n}\n\n/** Wrap an in-app region whose content is still loading: shows a centered spinner\n * (past the 150 ms floor) with `aria-busy`, then reveals `children`. For a shaped\n * wait, pass a `<Skeleton>` as `fallback` instead. App a11y only — not host chrome. */\nexport function LoadingRegion({\n loading,\n fallback,\n label = \"Loading\",\n children,\n}: {\n loading: boolean;\n fallback?: ReactNode;\n label?: string;\n children?: ReactNode;\n}) {\n useLoadingStyles();\n if (!loading) return <>{children}</>;\n return (\n <div\n aria-busy=\"true\"\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"center\", width: \"100%\", height: \"100%\", minHeight: 48 }}\n >\n {fallback ?? <Spinner label={label} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}