@matterfact/embed 0.16.0 → 0.18.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.
package/README.md CHANGED
@@ -92,6 +92,31 @@ reader has changed it.
92
92
  `onParamsChange` fires for reader- and page-context-driven changes, never for your own
93
93
  pushes — so feeding it straight back into `params` can't loop.
94
94
 
95
+ ### Finding out which params an artifact accepts
96
+
97
+ Params only work under the names the artifact declared in its manifest, and a name it
98
+ never declared is ignored rather than rejected — so a typo looks exactly like a working
99
+ integration.
100
+
101
+ **Open the browser console.** Every embedded artifact states what it accepts as soon as it
102
+ loads, before you have pushed anything:
103
+
104
+ ```
105
+ [matterfact] Artifact "snowflake-query-showcase" accepts params:
106
+ ticker (enum: AAPL US, default "AAPL US"); companyLike (enum: %APPLE%, default "%APPLE%").
107
+ ```
108
+
109
+ Read the **default** as well as the type: it is what reveals a convention the type can't.
110
+ Ticker format is per-artifact — some want a bare symbol (`MU`), others Bloomberg-style
111
+ (`AAPL US`), and none want a ` Equity` tail.
112
+
113
+ `declares no params` means the artifact has no `params` block at all. It isn't drivable as
114
+ it stands and whoever owns it needs to add one — no host-side change will help.
115
+
116
+ Push a name it doesn't declare and you also get a warning naming it, so a typo says so
117
+ rather than failing quietly. Values are normalised on arrival, so what a reader sees may
118
+ differ from what you sent: an `entity` is trimmed, upper-cased and capped at 24 characters.
119
+
95
120
  ### An artifact inside the chat
96
121
 
97
122
  ```js
@@ -0,0 +1,2 @@
1
+ var y="{share_token}",d="{path}",g="mf-share-token-sentinel",S=/^[A-Za-z0-9._~-]{4,256}$/,k=t=>t.length>1&&t.endsWith("/")?t.slice(0,-1):t;function w(t){return L(t)?.match??null}function L(t){if(t.includes(d))return x(t);let u=R(t);return u?{match:u,mintFormat:()=>t,dynamic:!1}:null}function x(t){let u=t.indexOf(d);if(t.indexOf(d,u+1)>=0)return null;let n=t.slice(0,u);n.endsWith("/")&&(n=n.slice(0,-1));let s=t.slice(u+d.length),c;try{c=new URL(n)}catch{return null}if(c.protocol!=="https:"&&c.protocol!=="http:"||c.origin!==n||!s.startsWith("?"))return null;let f=s.indexOf(y);if(f<0||s.indexOf(y,f+1)>=0)return null;let o;try{o=new URL(n+"/__mfp__"+s.replace(y,g))}catch{return null}let p=[...o.searchParams.keys()];if(new Set(p).size!==p.length)return null;let m=null;for(let[h,l]of o.searchParams)l===g&&(m=h);return m?{match:h=>{let l;try{l=new URL(h)}catch{return null}if(l.origin!==n)return null;for(let[i,e]of o.searchParams)if(e!==g&&l.searchParams.get(i)!==e)return null;let a=l.searchParams.get(m);return a&&S.test(a)?a:null},mintFormat:h=>n+h+s,dynamic:!0}:null}function R(t){let u=t.indexOf(y);if(u<0||t.indexOf(y,u+1)>=0)return null;let n;try{n=new URL(t.replace(y,g))}catch{return null}if(n.protocol!=="https:"&&n.protocol!=="http:")return null;let s=[...n.searchParams.keys()];if(new Set(s).size!==s.length)return null;let c=k(n.pathname),f=c.split("/"),o=f.indexOf(g),p=null;for(let[m,r]of n.searchParams)r===g&&(p=m);return o>=0==(p!==null)?null:m=>{let r;try{r=new URL(m)}catch{return null}if(r.origin!==n.origin)return null;let h=k(r.pathname),l;if(o>=0){let a=h.split("/");if(a.length!==f.length)return null;for(let i=0;i<f.length;i++)if(i!==o&&a[i]!==f[i])return null;l=a[o];for(let[i,e]of n.searchParams)if(r.searchParams.get(i)!==e)return null}else{if(h!==c)return null;for(let[a,i]of n.searchParams)if(i!==g&&r.searchParams.get(a)!==i)return null;l=r.searchParams.get(p)}return l&&S.test(l)?l:null}}function v(t,u,n){let s=L(t);if(!s)return()=>{};let c=null,f=null,o=null,p=!1,m=()=>{if(!n)return;let e=location.pathname;(o===null||s.dynamic&&e!==o)&&(o=e,n(s.mintFormat(e)))},r=()=>{if(!p)try{m();let e=s.match(location.href);if(e===null&&c!==null&&k(location.pathname)===f)return;e!==c&&(c=e,e!==null&&(f=k(location.pathname)),u(e))}catch(e){console.warn("[mf-embed] deeplink match failed",e)}},h=history.pushState,l=history.replaceState,a=function(...e){let P=h.apply(this,e);return r(),P},i=function(...e){let P=l.apply(this,e);return r(),P};return history.pushState=a,history.replaceState=i,window.addEventListener("popstate",r),r(),()=>{p=!0,window.removeEventListener("popstate",r),history.pushState===a&&(history.pushState=h),history.replaceState===i&&(history.replaceState=l)}}export{L as compileDeeplink,w as compileDeeplinkFormat,v as installDeeplinkWatch};
2
+ //# sourceMappingURL=chunk-5X2WFSIL.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/deeplink.ts"],
4
+ "sourcesContent": ["/**\n * Share-deeplink matching for the host-page loader. Lazy chunk \u2014 loaded only\n * when the widget reports a configured deeplink_format \u2014 so the eager stub\n * budget is untouched (see build.mjs STUB_BUDGET_GZ).\n *\n * Mirrors the backend's validate_deeplink_format contract: the {share_token}\n * placeholder is a whole path segment or a whole query value; literal template\n * query params must match; extra host params (utm noise) are ignored. A repeated\n * query key is read first-wins on the HREF (URLSearchParams.get), and refused\n * outright on the TEMPLATE \u2014 a template asking for two values of one key can\n * never match.\n */\nconst PLACEHOLDER = '{share_token}';\n/** Dynamic templates: `{path}` stands for the host page's CURRENT pathname \u2014 one\n * template mints links to (and picks up on) whatever page the widget is open over,\n * which is what a corner-popup install wants. It must sit directly after the origin\n * with the `{share_token}` in a query param: `https://host{path}?share={share_token}`. */\nconst PATH_TOKEN = '{path}';\n// URL() percent-encodes braces in paths \u2014 parse the template with a safe\n// sentinel standing in for the placeholder, then locate the sentinel.\nconst SENTINEL = 'mf-share-token-sentinel';\nconst TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;\n\nexport interface CompiledDeeplink {\n match: (href: string) => string | null;\n /** The mint-side template for a given live pathname: static templates are\n * themselves; `{path}` templates get the pathname spliced in. The\n * `{share_token}` placeholder always survives \u2014 the widget replaces it. */\n mintFormat: (pathname: string) => string;\n /** True when the template varies with the page (`{path}`) \u2014 the watch then\n * re-emits the mint format on every pathname change. */\n dynamic: boolean;\n}\n\n/** `/share/tok/` and `/share/tok` are the same route; one trailing empty segment\n * is noise, on the href and on the template alike. A second one is not. */\nconst trimSlash = (path: string) =>\n path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;\n\n/**\n * Turn a host's deeplink template into a matcher over `location.href`, or `null`\n * if the template is one we refuse to honour (no placeholder, two of them, one\n * that isn't a whole segment/value, a non-web scheme, a repeated query key). A\n * `null` here is a dead watch, never a match-everything.\n */\nexport function compileDeeplinkFormat(\n format: string,\n): ((href: string) => string | null) | null {\n return compileDeeplink(format)?.match ?? null;\n}\n\n/** The full compilation: matcher + mint-side resolver + dynamism flag. */\nexport function compileDeeplink(format: string): CompiledDeeplink | null {\n if (format.includes(PATH_TOKEN)) return compileDynamic(format);\n const match = compileStatic(format);\n return match ? { match, mintFormat: () => format, dynamic: false } : null;\n}\n\n/**\n * `{path}` templates: `<origin>{path}?\u2026{share_token}\u2026`. The prefix must be\n * exactly an origin (any pathname would be ambiguous against the wildcard) and\n * the token must be a whole query value (a wildcard path plus a path-segment\n * token isn't decodable).\n */\nfunction compileDynamic(format: string): CompiledDeeplink | null {\n const pi = format.indexOf(PATH_TOKEN);\n if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;\n // Tolerate `${origin}/{path}` \u2014 the pathname carries its own leading slash.\n let prefix = format.slice(0, pi);\n if (prefix.endsWith('/')) prefix = prefix.slice(0, -1);\n const suffix = format.slice(pi + PATH_TOKEN.length);\n let po: URL;\n try {\n po = new URL(prefix);\n } catch {\n return null;\n }\n if (po.protocol !== 'https:' && po.protocol !== 'http:') return null;\n if (po.origin !== prefix) return null;\n if (!suffix.startsWith('?')) return null;\n const c1 = suffix.indexOf(PLACEHOLDER);\n if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;\n let probe: URL;\n try {\n probe = new URL(prefix + '/__mfp__' + suffix.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n const keys = [...probe.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n let queryKey: string | null = null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // The placeholder must be a WHOLE query value; embedded in one is unmatchable.\n if (!queryKey) return null;\n const match = (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== prefix) return null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n const token = u.searchParams.get(queryKey!);\n return token && TOKEN_RE.test(token) ? token : null;\n };\n return {\n match,\n mintFormat: (pathname) => prefix + pathname + suffix,\n dynamic: true,\n };\n}\n\nfunction compileStatic(\n format: string,\n): ((href: string) => string | null) | null {\n const first = format.indexOf(PLACEHOLDER);\n if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;\n let f: URL;\n try {\n f = new URL(format.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n // Web schemes only, as the backend validator has it. A `file:`/opaque-origin\n // template would make the origin check below vacuous: every opaque origin\n // serialises to the same `\"null\"`, so any such page would match any such template.\n if (f.protocol !== 'https:' && f.protocol !== 'http:') return null;\n const keys = [...f.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n const fPath = trimSlash(f.pathname);\n const segs = fPath.split('/');\n const segIdx = segs.indexOf(SENTINEL);\n let queryKey: string | null = null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // Exactly one placement, and it must be a WHOLE segment/value \u2014 a partial\n // (\"/share-{share_token}\") leaves no sentinel match in either list.\n if ((segIdx >= 0) === (queryKey !== null)) return null;\n\n return (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== f.origin) return null;\n const uPath = trimSlash(u.pathname);\n let token: string | null;\n if (segIdx >= 0) {\n const us = uPath.split('/');\n if (us.length !== segs.length) return null;\n for (let i = 0; i < segs.length; i++) {\n if (i !== segIdx && us[i] !== segs[i]) return null;\n }\n // Deliberately NOT decoded. TOKEN_RE admits only unreserved characters, so a\n // percent-escape is a non-token either way \u2014 while decodeURIComponent throws\n // URIError on a malformed one (`/share/100%off`), inside the host's own pushState.\n token = us[segIdx];\n for (const [k, v] of f.searchParams) {\n if (u.searchParams.get(k) !== v) return null;\n }\n } else {\n if (uPath !== fPath) return null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n token = u.searchParams.get(queryKey!);\n }\n return token && TOKEN_RE.test(token) ? token : null;\n };\n}\n\n/**\n * Watch the host location for the template, reporting the token on every\n * transition \u2014 including the transition to `null` when the user navigates away,\n * which is how the widget learns to stop showing a shared thread. Never fires an\n * initial `null`: a page that was never a deeplink has nothing to report. A\n * query-only strip on the SAME pathname (app shells tidying their URL on boot)\n * keeps the token \u2014 the user didn't leave the page.\n *\n * `onFormat` receives the mint-side template: once at install for static\n * templates, and on every pathname change for `{path}` ones \u2014 the loader relays\n * it to the widget so copied links always name the page the sharer is on.\n *\n * Returns an uninstall. An unmatchable format gets a no-op one rather than a\n * watch that can never fire.\n */\nexport function installDeeplinkWatch(\n format: string,\n onChange: (token: string | null) => void,\n onFormat?: (format: string) => void,\n): () => void {\n const c = compileDeeplink(format);\n if (!c) return () => {};\n let last: string | null = null;\n /** The (slash-trimmed) pathname where `last` was seen. App shells commonly\n * normalise the URL on boot and strip query params they don't know \u2014 if the\n * token vanishes while the pathname hasn't changed, that's a tidy-up, not a\n * navigation, and the shared view must survive it. */\n let lastTokenPath: string | null = null;\n let lastMintPath: string | null = null;\n let stopped = false;\n const emitFormat = () => {\n if (!onFormat) return;\n const p = location.pathname;\n if (lastMintPath === null || (c.dynamic && p !== lastMintPath)) {\n lastMintPath = p;\n onFormat(c.mintFormat(p));\n }\n };\n const fire = () => {\n // We run INSIDE the host's own pushState. Nothing in here may surface as an\n // exception in their router, so the whole body is guarded \u2014 and `stopped` neuters\n // this closure for good, including via a wrapper someone else's restore put back.\n if (stopped) return;\n try {\n // Format before token: messages arrive in order, so the widget always\n // holds the current mint template before it could act on a pickup.\n emitFormat();\n const token = c.match(location.href);\n if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {\n return; // param stripped in place \u2014 keep the shared view\n }\n if (token !== last) {\n last = token;\n if (token !== null) lastTokenPath = trimSlash(location.pathname);\n onChange(token);\n }\n } catch (err) {\n // Swallowed \u2014 a matcher bug, or a throwing consumer, is ours, not the host's \u2014\n // but surfaced, since this chunk already loaded and the byte is free here.\n console.warn('[mf-embed] deeplink match failed', err);\n }\n };\n // Wrap history like context.ts's watchNavigation does, but independently \u2014 this\n // watch must run even when page-context observation is off.\n const origPush = history.pushState;\n const origReplace = history.replaceState;\n const ourPush = function (\n this: History,\n ...args: Parameters<History['pushState']>\n ) {\n const r = origPush.apply(this, args);\n fire();\n return r;\n } as History['pushState'];\n const ourReplace = function (\n this: History,\n ...args: Parameters<History['replaceState']>\n ) {\n const r = origReplace.apply(this, args);\n fire();\n return r;\n } as History['replaceState'];\n history.pushState = ourPush;\n history.replaceState = ourReplace;\n window.addEventListener('popstate', fire);\n fire();\n return () => {\n stopped = true;\n window.removeEventListener('popstate', fire);\n // Restore only while OUR wrapper is still the outermost one. context.ts wraps on\n // first chat open, on top of us: assigning over it would drop its wrapper, and its\n // own restore would later hand ours back \u2014 which is what `stopped` is for.\n if (history.pushState === ourPush) history.pushState = origPush;\n if (history.replaceState === ourReplace) history.replaceState = origReplace;\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["PLACEHOLDER", "PATH_TOKEN", "SENTINEL", "TOKEN_RE", "trimSlash", "path", "compileDeeplinkFormat", "format", "compileDeeplink", "compileDynamic", "match", "compileStatic", "pi", "prefix", "suffix", "po", "c1", "probe", "keys", "queryKey", "k", "v", "href", "u", "token", "pathname", "first", "f", "fPath", "segs", "segIdx", "uPath", "us", "installDeeplinkWatch", "onChange", "onFormat", "c", "last", "lastTokenPath", "lastMintPath", "stopped", "emitFormat", "p", "fire", "err", "origPush", "origReplace", "ourPush", "args", "r", "ourReplace"]
7
+ }
@@ -0,0 +1,180 @@
1
+ "use client";
2
+
3
+ // src/deeplink.ts
4
+ var PLACEHOLDER = "{share_token}";
5
+ var PATH_TOKEN = "{path}";
6
+ var SENTINEL = "mf-share-token-sentinel";
7
+ var TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;
8
+ var trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
9
+ function compileDeeplinkFormat(format) {
10
+ return compileDeeplink(format)?.match ?? null;
11
+ }
12
+ function compileDeeplink(format) {
13
+ if (format.includes(PATH_TOKEN)) return compileDynamic(format);
14
+ const match = compileStatic(format);
15
+ return match ? { match, mintFormat: () => format, dynamic: false } : null;
16
+ }
17
+ function compileDynamic(format) {
18
+ const pi = format.indexOf(PATH_TOKEN);
19
+ if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;
20
+ let prefix = format.slice(0, pi);
21
+ if (prefix.endsWith("/")) prefix = prefix.slice(0, -1);
22
+ const suffix = format.slice(pi + PATH_TOKEN.length);
23
+ let po;
24
+ try {
25
+ po = new URL(prefix);
26
+ } catch {
27
+ return null;
28
+ }
29
+ if (po.protocol !== "https:" && po.protocol !== "http:") return null;
30
+ if (po.origin !== prefix) return null;
31
+ if (!suffix.startsWith("?")) return null;
32
+ const c1 = suffix.indexOf(PLACEHOLDER);
33
+ if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;
34
+ let probe;
35
+ try {
36
+ probe = new URL(prefix + "/__mfp__" + suffix.replace(PLACEHOLDER, SENTINEL));
37
+ } catch {
38
+ return null;
39
+ }
40
+ const keys = [...probe.searchParams.keys()];
41
+ if (new Set(keys).size !== keys.length) return null;
42
+ let queryKey = null;
43
+ for (const [k, v] of probe.searchParams) {
44
+ if (v === SENTINEL) queryKey = k;
45
+ }
46
+ if (!queryKey) return null;
47
+ const match = (href) => {
48
+ let u;
49
+ try {
50
+ u = new URL(href);
51
+ } catch {
52
+ return null;
53
+ }
54
+ if (u.origin !== prefix) return null;
55
+ for (const [k, v] of probe.searchParams) {
56
+ if (v === SENTINEL) continue;
57
+ if (u.searchParams.get(k) !== v) return null;
58
+ }
59
+ const token = u.searchParams.get(queryKey);
60
+ return token && TOKEN_RE.test(token) ? token : null;
61
+ };
62
+ return {
63
+ match,
64
+ mintFormat: (pathname) => prefix + pathname + suffix,
65
+ dynamic: true
66
+ };
67
+ }
68
+ function compileStatic(format) {
69
+ const first = format.indexOf(PLACEHOLDER);
70
+ if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;
71
+ let f;
72
+ try {
73
+ f = new URL(format.replace(PLACEHOLDER, SENTINEL));
74
+ } catch {
75
+ return null;
76
+ }
77
+ if (f.protocol !== "https:" && f.protocol !== "http:") return null;
78
+ const keys = [...f.searchParams.keys()];
79
+ if (new Set(keys).size !== keys.length) return null;
80
+ const fPath = trimSlash(f.pathname);
81
+ const segs = fPath.split("/");
82
+ const segIdx = segs.indexOf(SENTINEL);
83
+ let queryKey = null;
84
+ for (const [k, v] of f.searchParams) {
85
+ if (v === SENTINEL) queryKey = k;
86
+ }
87
+ if (segIdx >= 0 === (queryKey !== null)) return null;
88
+ return (href) => {
89
+ let u;
90
+ try {
91
+ u = new URL(href);
92
+ } catch {
93
+ return null;
94
+ }
95
+ if (u.origin !== f.origin) return null;
96
+ const uPath = trimSlash(u.pathname);
97
+ let token;
98
+ if (segIdx >= 0) {
99
+ const us = uPath.split("/");
100
+ if (us.length !== segs.length) return null;
101
+ for (let i = 0; i < segs.length; i++) {
102
+ if (i !== segIdx && us[i] !== segs[i]) return null;
103
+ }
104
+ token = us[segIdx];
105
+ for (const [k, v] of f.searchParams) {
106
+ if (u.searchParams.get(k) !== v) return null;
107
+ }
108
+ } else {
109
+ if (uPath !== fPath) return null;
110
+ for (const [k, v] of f.searchParams) {
111
+ if (v === SENTINEL) continue;
112
+ if (u.searchParams.get(k) !== v) return null;
113
+ }
114
+ token = u.searchParams.get(queryKey);
115
+ }
116
+ return token && TOKEN_RE.test(token) ? token : null;
117
+ };
118
+ }
119
+ function installDeeplinkWatch(format, onChange, onFormat) {
120
+ const c = compileDeeplink(format);
121
+ if (!c) return () => {
122
+ };
123
+ let last = null;
124
+ let lastTokenPath = null;
125
+ let lastMintPath = null;
126
+ let stopped = false;
127
+ const emitFormat = () => {
128
+ if (!onFormat) return;
129
+ const p = location.pathname;
130
+ if (lastMintPath === null || c.dynamic && p !== lastMintPath) {
131
+ lastMintPath = p;
132
+ onFormat(c.mintFormat(p));
133
+ }
134
+ };
135
+ const fire = () => {
136
+ if (stopped) return;
137
+ try {
138
+ emitFormat();
139
+ const token = c.match(location.href);
140
+ if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {
141
+ return;
142
+ }
143
+ if (token !== last) {
144
+ last = token;
145
+ if (token !== null) lastTokenPath = trimSlash(location.pathname);
146
+ onChange(token);
147
+ }
148
+ } catch (err) {
149
+ console.warn("[mf-embed] deeplink match failed", err);
150
+ }
151
+ };
152
+ const origPush = history.pushState;
153
+ const origReplace = history.replaceState;
154
+ const ourPush = function(...args) {
155
+ const r = origPush.apply(this, args);
156
+ fire();
157
+ return r;
158
+ };
159
+ const ourReplace = function(...args) {
160
+ const r = origReplace.apply(this, args);
161
+ fire();
162
+ return r;
163
+ };
164
+ history.pushState = ourPush;
165
+ history.replaceState = ourReplace;
166
+ window.addEventListener("popstate", fire);
167
+ fire();
168
+ return () => {
169
+ stopped = true;
170
+ window.removeEventListener("popstate", fire);
171
+ if (history.pushState === ourPush) history.pushState = origPush;
172
+ if (history.replaceState === ourReplace) history.replaceState = origReplace;
173
+ };
174
+ }
175
+ export {
176
+ compileDeeplink,
177
+ compileDeeplinkFormat,
178
+ installDeeplinkWatch
179
+ };
180
+ //# sourceMappingURL=deeplink-KCGMGNIR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/deeplink.ts"],"sourcesContent":["/**\n * Share-deeplink matching for the host-page loader. Lazy chunk — loaded only\n * when the widget reports a configured deeplink_format — so the eager stub\n * budget is untouched (see build.mjs STUB_BUDGET_GZ).\n *\n * Mirrors the backend's validate_deeplink_format contract: the {share_token}\n * placeholder is a whole path segment or a whole query value; literal template\n * query params must match; extra host params (utm noise) are ignored. A repeated\n * query key is read first-wins on the HREF (URLSearchParams.get), and refused\n * outright on the TEMPLATE — a template asking for two values of one key can\n * never match.\n */\nconst PLACEHOLDER = '{share_token}';\n/** Dynamic templates: `{path}` stands for the host page's CURRENT pathname — one\n * template mints links to (and picks up on) whatever page the widget is open over,\n * which is what a corner-popup install wants. It must sit directly after the origin\n * with the `{share_token}` in a query param: `https://host{path}?share={share_token}`. */\nconst PATH_TOKEN = '{path}';\n// URL() percent-encodes braces in paths — parse the template with a safe\n// sentinel standing in for the placeholder, then locate the sentinel.\nconst SENTINEL = 'mf-share-token-sentinel';\nconst TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;\n\nexport interface CompiledDeeplink {\n match: (href: string) => string | null;\n /** The mint-side template for a given live pathname: static templates are\n * themselves; `{path}` templates get the pathname spliced in. The\n * `{share_token}` placeholder always survives — the widget replaces it. */\n mintFormat: (pathname: string) => string;\n /** True when the template varies with the page (`{path}`) — the watch then\n * re-emits the mint format on every pathname change. */\n dynamic: boolean;\n}\n\n/** `/share/tok/` and `/share/tok` are the same route; one trailing empty segment\n * is noise, on the href and on the template alike. A second one is not. */\nconst trimSlash = (path: string) =>\n path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;\n\n/**\n * Turn a host's deeplink template into a matcher over `location.href`, or `null`\n * if the template is one we refuse to honour (no placeholder, two of them, one\n * that isn't a whole segment/value, a non-web scheme, a repeated query key). A\n * `null` here is a dead watch, never a match-everything.\n */\nexport function compileDeeplinkFormat(\n format: string,\n): ((href: string) => string | null) | null {\n return compileDeeplink(format)?.match ?? null;\n}\n\n/** The full compilation: matcher + mint-side resolver + dynamism flag. */\nexport function compileDeeplink(format: string): CompiledDeeplink | null {\n if (format.includes(PATH_TOKEN)) return compileDynamic(format);\n const match = compileStatic(format);\n return match ? { match, mintFormat: () => format, dynamic: false } : null;\n}\n\n/**\n * `{path}` templates: `<origin>{path}?…{share_token}…`. The prefix must be\n * exactly an origin (any pathname would be ambiguous against the wildcard) and\n * the token must be a whole query value (a wildcard path plus a path-segment\n * token isn't decodable).\n */\nfunction compileDynamic(format: string): CompiledDeeplink | null {\n const pi = format.indexOf(PATH_TOKEN);\n if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;\n // Tolerate `${origin}/{path}` — the pathname carries its own leading slash.\n let prefix = format.slice(0, pi);\n if (prefix.endsWith('/')) prefix = prefix.slice(0, -1);\n const suffix = format.slice(pi + PATH_TOKEN.length);\n let po: URL;\n try {\n po = new URL(prefix);\n } catch {\n return null;\n }\n if (po.protocol !== 'https:' && po.protocol !== 'http:') return null;\n if (po.origin !== prefix) return null;\n if (!suffix.startsWith('?')) return null;\n const c1 = suffix.indexOf(PLACEHOLDER);\n if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;\n let probe: URL;\n try {\n probe = new URL(prefix + '/__mfp__' + suffix.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n const keys = [...probe.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n let queryKey: string | null = null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // The placeholder must be a WHOLE query value; embedded in one is unmatchable.\n if (!queryKey) return null;\n const match = (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== prefix) return null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n const token = u.searchParams.get(queryKey!);\n return token && TOKEN_RE.test(token) ? token : null;\n };\n return {\n match,\n mintFormat: (pathname) => prefix + pathname + suffix,\n dynamic: true,\n };\n}\n\nfunction compileStatic(\n format: string,\n): ((href: string) => string | null) | null {\n const first = format.indexOf(PLACEHOLDER);\n if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;\n let f: URL;\n try {\n f = new URL(format.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n // Web schemes only, as the backend validator has it. A `file:`/opaque-origin\n // template would make the origin check below vacuous: every opaque origin\n // serialises to the same `\"null\"`, so any such page would match any such template.\n if (f.protocol !== 'https:' && f.protocol !== 'http:') return null;\n const keys = [...f.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n const fPath = trimSlash(f.pathname);\n const segs = fPath.split('/');\n const segIdx = segs.indexOf(SENTINEL);\n let queryKey: string | null = null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // Exactly one placement, and it must be a WHOLE segment/value — a partial\n // (\"/share-{share_token}\") leaves no sentinel match in either list.\n if ((segIdx >= 0) === (queryKey !== null)) return null;\n\n return (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== f.origin) return null;\n const uPath = trimSlash(u.pathname);\n let token: string | null;\n if (segIdx >= 0) {\n const us = uPath.split('/');\n if (us.length !== segs.length) return null;\n for (let i = 0; i < segs.length; i++) {\n if (i !== segIdx && us[i] !== segs[i]) return null;\n }\n // Deliberately NOT decoded. TOKEN_RE admits only unreserved characters, so a\n // percent-escape is a non-token either way — while decodeURIComponent throws\n // URIError on a malformed one (`/share/100%off`), inside the host's own pushState.\n token = us[segIdx];\n for (const [k, v] of f.searchParams) {\n if (u.searchParams.get(k) !== v) return null;\n }\n } else {\n if (uPath !== fPath) return null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n token = u.searchParams.get(queryKey!);\n }\n return token && TOKEN_RE.test(token) ? token : null;\n };\n}\n\n/**\n * Watch the host location for the template, reporting the token on every\n * transition — including the transition to `null` when the user navigates away,\n * which is how the widget learns to stop showing a shared thread. Never fires an\n * initial `null`: a page that was never a deeplink has nothing to report. A\n * query-only strip on the SAME pathname (app shells tidying their URL on boot)\n * keeps the token — the user didn't leave the page.\n *\n * `onFormat` receives the mint-side template: once at install for static\n * templates, and on every pathname change for `{path}` ones — the loader relays\n * it to the widget so copied links always name the page the sharer is on.\n *\n * Returns an uninstall. An unmatchable format gets a no-op one rather than a\n * watch that can never fire.\n */\nexport function installDeeplinkWatch(\n format: string,\n onChange: (token: string | null) => void,\n onFormat?: (format: string) => void,\n): () => void {\n const c = compileDeeplink(format);\n if (!c) return () => {};\n let last: string | null = null;\n /** The (slash-trimmed) pathname where `last` was seen. App shells commonly\n * normalise the URL on boot and strip query params they don't know — if the\n * token vanishes while the pathname hasn't changed, that's a tidy-up, not a\n * navigation, and the shared view must survive it. */\n let lastTokenPath: string | null = null;\n let lastMintPath: string | null = null;\n let stopped = false;\n const emitFormat = () => {\n if (!onFormat) return;\n const p = location.pathname;\n if (lastMintPath === null || (c.dynamic && p !== lastMintPath)) {\n lastMintPath = p;\n onFormat(c.mintFormat(p));\n }\n };\n const fire = () => {\n // We run INSIDE the host's own pushState. Nothing in here may surface as an\n // exception in their router, so the whole body is guarded — and `stopped` neuters\n // this closure for good, including via a wrapper someone else's restore put back.\n if (stopped) return;\n try {\n // Format before token: messages arrive in order, so the widget always\n // holds the current mint template before it could act on a pickup.\n emitFormat();\n const token = c.match(location.href);\n if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {\n return; // param stripped in place — keep the shared view\n }\n if (token !== last) {\n last = token;\n if (token !== null) lastTokenPath = trimSlash(location.pathname);\n onChange(token);\n }\n } catch (err) {\n // Swallowed — a matcher bug, or a throwing consumer, is ours, not the host's —\n // but surfaced, since this chunk already loaded and the byte is free here.\n console.warn('[mf-embed] deeplink match failed', err);\n }\n };\n // Wrap history like context.ts's watchNavigation does, but independently — this\n // watch must run even when page-context observation is off.\n const origPush = history.pushState;\n const origReplace = history.replaceState;\n const ourPush = function (\n this: History,\n ...args: Parameters<History['pushState']>\n ) {\n const r = origPush.apply(this, args);\n fire();\n return r;\n } as History['pushState'];\n const ourReplace = function (\n this: History,\n ...args: Parameters<History['replaceState']>\n ) {\n const r = origReplace.apply(this, args);\n fire();\n return r;\n } as History['replaceState'];\n history.pushState = ourPush;\n history.replaceState = ourReplace;\n window.addEventListener('popstate', fire);\n fire();\n return () => {\n stopped = true;\n window.removeEventListener('popstate', fire);\n // Restore only while OUR wrapper is still the outermost one. context.ts wraps on\n // first chat open, on top of us: assigning over it would drop its wrapper, and its\n // own restore would later hand ours back — which is what `stopped` is for.\n if (history.pushState === ourPush) history.pushState = origPush;\n if (history.replaceState === ourReplace) history.replaceState = origReplace;\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,176 @@
1
+ // src/deeplink.ts
2
+ var PLACEHOLDER = "{share_token}";
3
+ var PATH_TOKEN = "{path}";
4
+ var SENTINEL = "mf-share-token-sentinel";
5
+ var TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;
6
+ var trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
7
+ function compileDeeplinkFormat(format) {
8
+ return compileDeeplink(format)?.match ?? null;
9
+ }
10
+ function compileDeeplink(format) {
11
+ if (format.includes(PATH_TOKEN)) return compileDynamic(format);
12
+ const match = compileStatic(format);
13
+ return match ? { match, mintFormat: () => format, dynamic: false } : null;
14
+ }
15
+ function compileDynamic(format) {
16
+ const pi = format.indexOf(PATH_TOKEN);
17
+ if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;
18
+ let prefix = format.slice(0, pi);
19
+ if (prefix.endsWith("/")) prefix = prefix.slice(0, -1);
20
+ const suffix = format.slice(pi + PATH_TOKEN.length);
21
+ let po;
22
+ try {
23
+ po = new URL(prefix);
24
+ } catch {
25
+ return null;
26
+ }
27
+ if (po.protocol !== "https:" && po.protocol !== "http:") return null;
28
+ if (po.origin !== prefix) return null;
29
+ if (!suffix.startsWith("?")) return null;
30
+ const c1 = suffix.indexOf(PLACEHOLDER);
31
+ if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;
32
+ let probe;
33
+ try {
34
+ probe = new URL(prefix + "/__mfp__" + suffix.replace(PLACEHOLDER, SENTINEL));
35
+ } catch {
36
+ return null;
37
+ }
38
+ const keys = [...probe.searchParams.keys()];
39
+ if (new Set(keys).size !== keys.length) return null;
40
+ let queryKey = null;
41
+ for (const [k, v] of probe.searchParams) {
42
+ if (v === SENTINEL) queryKey = k;
43
+ }
44
+ if (!queryKey) return null;
45
+ const match = (href) => {
46
+ let u;
47
+ try {
48
+ u = new URL(href);
49
+ } catch {
50
+ return null;
51
+ }
52
+ if (u.origin !== prefix) return null;
53
+ for (const [k, v] of probe.searchParams) {
54
+ if (v === SENTINEL) continue;
55
+ if (u.searchParams.get(k) !== v) return null;
56
+ }
57
+ const token = u.searchParams.get(queryKey);
58
+ return token && TOKEN_RE.test(token) ? token : null;
59
+ };
60
+ return {
61
+ match,
62
+ mintFormat: (pathname) => prefix + pathname + suffix,
63
+ dynamic: true
64
+ };
65
+ }
66
+ function compileStatic(format) {
67
+ const first = format.indexOf(PLACEHOLDER);
68
+ if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;
69
+ let f;
70
+ try {
71
+ f = new URL(format.replace(PLACEHOLDER, SENTINEL));
72
+ } catch {
73
+ return null;
74
+ }
75
+ if (f.protocol !== "https:" && f.protocol !== "http:") return null;
76
+ const keys = [...f.searchParams.keys()];
77
+ if (new Set(keys).size !== keys.length) return null;
78
+ const fPath = trimSlash(f.pathname);
79
+ const segs = fPath.split("/");
80
+ const segIdx = segs.indexOf(SENTINEL);
81
+ let queryKey = null;
82
+ for (const [k, v] of f.searchParams) {
83
+ if (v === SENTINEL) queryKey = k;
84
+ }
85
+ if (segIdx >= 0 === (queryKey !== null)) return null;
86
+ return (href) => {
87
+ let u;
88
+ try {
89
+ u = new URL(href);
90
+ } catch {
91
+ return null;
92
+ }
93
+ if (u.origin !== f.origin) return null;
94
+ const uPath = trimSlash(u.pathname);
95
+ let token;
96
+ if (segIdx >= 0) {
97
+ const us = uPath.split("/");
98
+ if (us.length !== segs.length) return null;
99
+ for (let i = 0; i < segs.length; i++) {
100
+ if (i !== segIdx && us[i] !== segs[i]) return null;
101
+ }
102
+ token = us[segIdx];
103
+ for (const [k, v] of f.searchParams) {
104
+ if (u.searchParams.get(k) !== v) return null;
105
+ }
106
+ } else {
107
+ if (uPath !== fPath) return null;
108
+ for (const [k, v] of f.searchParams) {
109
+ if (v === SENTINEL) continue;
110
+ if (u.searchParams.get(k) !== v) return null;
111
+ }
112
+ token = u.searchParams.get(queryKey);
113
+ }
114
+ return token && TOKEN_RE.test(token) ? token : null;
115
+ };
116
+ }
117
+ function installDeeplinkWatch(format, onChange, onFormat) {
118
+ const c = compileDeeplink(format);
119
+ if (!c) return () => {
120
+ };
121
+ let last = null;
122
+ let lastTokenPath = null;
123
+ let lastMintPath = null;
124
+ let stopped = false;
125
+ const emitFormat = () => {
126
+ if (!onFormat) return;
127
+ const p = location.pathname;
128
+ if (lastMintPath === null || c.dynamic && p !== lastMintPath) {
129
+ lastMintPath = p;
130
+ onFormat(c.mintFormat(p));
131
+ }
132
+ };
133
+ const fire = () => {
134
+ if (stopped) return;
135
+ try {
136
+ emitFormat();
137
+ const token = c.match(location.href);
138
+ if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {
139
+ return;
140
+ }
141
+ if (token !== last) {
142
+ last = token;
143
+ if (token !== null) lastTokenPath = trimSlash(location.pathname);
144
+ onChange(token);
145
+ }
146
+ } catch (err) {
147
+ console.warn("[mf-embed] deeplink match failed", err);
148
+ }
149
+ };
150
+ const origPush = history.pushState;
151
+ const origReplace = history.replaceState;
152
+ const ourPush = function(...args) {
153
+ const r = origPush.apply(this, args);
154
+ fire();
155
+ return r;
156
+ };
157
+ const ourReplace = function(...args) {
158
+ const r = origReplace.apply(this, args);
159
+ fire();
160
+ return r;
161
+ };
162
+ history.pushState = ourPush;
163
+ history.replaceState = ourReplace;
164
+ window.addEventListener("popstate", fire);
165
+ fire();
166
+ return () => {
167
+ stopped = true;
168
+ window.removeEventListener("popstate", fire);
169
+ if (history.pushState === ourPush) history.pushState = origPush;
170
+ if (history.replaceState === ourReplace) history.replaceState = origReplace;
171
+ };
172
+ }
173
+
174
+ export { compileDeeplink, compileDeeplinkFormat, installDeeplinkWatch };
175
+ //# sourceMappingURL=deeplink-X7IQDBCR.js.map
176
+ //# sourceMappingURL=deeplink-X7IQDBCR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/deeplink.ts"],"names":[],"mappings":"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","file":"deeplink-X7IQDBCR.js","sourcesContent":["/**\n * Share-deeplink matching for the host-page loader. Lazy chunk — loaded only\n * when the widget reports a configured deeplink_format — so the eager stub\n * budget is untouched (see build.mjs STUB_BUDGET_GZ).\n *\n * Mirrors the backend's validate_deeplink_format contract: the {share_token}\n * placeholder is a whole path segment or a whole query value; literal template\n * query params must match; extra host params (utm noise) are ignored. A repeated\n * query key is read first-wins on the HREF (URLSearchParams.get), and refused\n * outright on the TEMPLATE — a template asking for two values of one key can\n * never match.\n */\nconst PLACEHOLDER = '{share_token}';\n/** Dynamic templates: `{path}` stands for the host page's CURRENT pathname — one\n * template mints links to (and picks up on) whatever page the widget is open over,\n * which is what a corner-popup install wants. It must sit directly after the origin\n * with the `{share_token}` in a query param: `https://host{path}?share={share_token}`. */\nconst PATH_TOKEN = '{path}';\n// URL() percent-encodes braces in paths — parse the template with a safe\n// sentinel standing in for the placeholder, then locate the sentinel.\nconst SENTINEL = 'mf-share-token-sentinel';\nconst TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;\n\nexport interface CompiledDeeplink {\n match: (href: string) => string | null;\n /** The mint-side template for a given live pathname: static templates are\n * themselves; `{path}` templates get the pathname spliced in. The\n * `{share_token}` placeholder always survives — the widget replaces it. */\n mintFormat: (pathname: string) => string;\n /** True when the template varies with the page (`{path}`) — the watch then\n * re-emits the mint format on every pathname change. */\n dynamic: boolean;\n}\n\n/** `/share/tok/` and `/share/tok` are the same route; one trailing empty segment\n * is noise, on the href and on the template alike. A second one is not. */\nconst trimSlash = (path: string) =>\n path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;\n\n/**\n * Turn a host's deeplink template into a matcher over `location.href`, or `null`\n * if the template is one we refuse to honour (no placeholder, two of them, one\n * that isn't a whole segment/value, a non-web scheme, a repeated query key). A\n * `null` here is a dead watch, never a match-everything.\n */\nexport function compileDeeplinkFormat(\n format: string,\n): ((href: string) => string | null) | null {\n return compileDeeplink(format)?.match ?? null;\n}\n\n/** The full compilation: matcher + mint-side resolver + dynamism flag. */\nexport function compileDeeplink(format: string): CompiledDeeplink | null {\n if (format.includes(PATH_TOKEN)) return compileDynamic(format);\n const match = compileStatic(format);\n return match ? { match, mintFormat: () => format, dynamic: false } : null;\n}\n\n/**\n * `{path}` templates: `<origin>{path}?…{share_token}…`. The prefix must be\n * exactly an origin (any pathname would be ambiguous against the wildcard) and\n * the token must be a whole query value (a wildcard path plus a path-segment\n * token isn't decodable).\n */\nfunction compileDynamic(format: string): CompiledDeeplink | null {\n const pi = format.indexOf(PATH_TOKEN);\n if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;\n // Tolerate `${origin}/{path}` — the pathname carries its own leading slash.\n let prefix = format.slice(0, pi);\n if (prefix.endsWith('/')) prefix = prefix.slice(0, -1);\n const suffix = format.slice(pi + PATH_TOKEN.length);\n let po: URL;\n try {\n po = new URL(prefix);\n } catch {\n return null;\n }\n if (po.protocol !== 'https:' && po.protocol !== 'http:') return null;\n if (po.origin !== prefix) return null;\n if (!suffix.startsWith('?')) return null;\n const c1 = suffix.indexOf(PLACEHOLDER);\n if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;\n let probe: URL;\n try {\n probe = new URL(prefix + '/__mfp__' + suffix.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n const keys = [...probe.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n let queryKey: string | null = null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // The placeholder must be a WHOLE query value; embedded in one is unmatchable.\n if (!queryKey) return null;\n const match = (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== prefix) return null;\n for (const [k, v] of probe.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n const token = u.searchParams.get(queryKey!);\n return token && TOKEN_RE.test(token) ? token : null;\n };\n return {\n match,\n mintFormat: (pathname) => prefix + pathname + suffix,\n dynamic: true,\n };\n}\n\nfunction compileStatic(\n format: string,\n): ((href: string) => string | null) | null {\n const first = format.indexOf(PLACEHOLDER);\n if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;\n let f: URL;\n try {\n f = new URL(format.replace(PLACEHOLDER, SENTINEL));\n } catch {\n return null;\n }\n // Web schemes only, as the backend validator has it. A `file:`/opaque-origin\n // template would make the origin check below vacuous: every opaque origin\n // serialises to the same `\"null\"`, so any such page would match any such template.\n if (f.protocol !== 'https:' && f.protocol !== 'http:') return null;\n const keys = [...f.searchParams.keys()];\n if (new Set(keys).size !== keys.length) return null;\n const fPath = trimSlash(f.pathname);\n const segs = fPath.split('/');\n const segIdx = segs.indexOf(SENTINEL);\n let queryKey: string | null = null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) queryKey = k;\n }\n // Exactly one placement, and it must be a WHOLE segment/value — a partial\n // (\"/share-{share_token}\") leaves no sentinel match in either list.\n if ((segIdx >= 0) === (queryKey !== null)) return null;\n\n return (href: string) => {\n let u: URL;\n try {\n u = new URL(href);\n } catch {\n return null;\n }\n if (u.origin !== f.origin) return null;\n const uPath = trimSlash(u.pathname);\n let token: string | null;\n if (segIdx >= 0) {\n const us = uPath.split('/');\n if (us.length !== segs.length) return null;\n for (let i = 0; i < segs.length; i++) {\n if (i !== segIdx && us[i] !== segs[i]) return null;\n }\n // Deliberately NOT decoded. TOKEN_RE admits only unreserved characters, so a\n // percent-escape is a non-token either way — while decodeURIComponent throws\n // URIError on a malformed one (`/share/100%off`), inside the host's own pushState.\n token = us[segIdx];\n for (const [k, v] of f.searchParams) {\n if (u.searchParams.get(k) !== v) return null;\n }\n } else {\n if (uPath !== fPath) return null;\n for (const [k, v] of f.searchParams) {\n if (v === SENTINEL) continue;\n if (u.searchParams.get(k) !== v) return null;\n }\n token = u.searchParams.get(queryKey!);\n }\n return token && TOKEN_RE.test(token) ? token : null;\n };\n}\n\n/**\n * Watch the host location for the template, reporting the token on every\n * transition — including the transition to `null` when the user navigates away,\n * which is how the widget learns to stop showing a shared thread. Never fires an\n * initial `null`: a page that was never a deeplink has nothing to report. A\n * query-only strip on the SAME pathname (app shells tidying their URL on boot)\n * keeps the token — the user didn't leave the page.\n *\n * `onFormat` receives the mint-side template: once at install for static\n * templates, and on every pathname change for `{path}` ones — the loader relays\n * it to the widget so copied links always name the page the sharer is on.\n *\n * Returns an uninstall. An unmatchable format gets a no-op one rather than a\n * watch that can never fire.\n */\nexport function installDeeplinkWatch(\n format: string,\n onChange: (token: string | null) => void,\n onFormat?: (format: string) => void,\n): () => void {\n const c = compileDeeplink(format);\n if (!c) return () => {};\n let last: string | null = null;\n /** The (slash-trimmed) pathname where `last` was seen. App shells commonly\n * normalise the URL on boot and strip query params they don't know — if the\n * token vanishes while the pathname hasn't changed, that's a tidy-up, not a\n * navigation, and the shared view must survive it. */\n let lastTokenPath: string | null = null;\n let lastMintPath: string | null = null;\n let stopped = false;\n const emitFormat = () => {\n if (!onFormat) return;\n const p = location.pathname;\n if (lastMintPath === null || (c.dynamic && p !== lastMintPath)) {\n lastMintPath = p;\n onFormat(c.mintFormat(p));\n }\n };\n const fire = () => {\n // We run INSIDE the host's own pushState. Nothing in here may surface as an\n // exception in their router, so the whole body is guarded — and `stopped` neuters\n // this closure for good, including via a wrapper someone else's restore put back.\n if (stopped) return;\n try {\n // Format before token: messages arrive in order, so the widget always\n // holds the current mint template before it could act on a pickup.\n emitFormat();\n const token = c.match(location.href);\n if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {\n return; // param stripped in place — keep the shared view\n }\n if (token !== last) {\n last = token;\n if (token !== null) lastTokenPath = trimSlash(location.pathname);\n onChange(token);\n }\n } catch (err) {\n // Swallowed — a matcher bug, or a throwing consumer, is ours, not the host's —\n // but surfaced, since this chunk already loaded and the byte is free here.\n console.warn('[mf-embed] deeplink match failed', err);\n }\n };\n // Wrap history like context.ts's watchNavigation does, but independently — this\n // watch must run even when page-context observation is off.\n const origPush = history.pushState;\n const origReplace = history.replaceState;\n const ourPush = function (\n this: History,\n ...args: Parameters<History['pushState']>\n ) {\n const r = origPush.apply(this, args);\n fire();\n return r;\n } as History['pushState'];\n const ourReplace = function (\n this: History,\n ...args: Parameters<History['replaceState']>\n ) {\n const r = origReplace.apply(this, args);\n fire();\n return r;\n } as History['replaceState'];\n history.pushState = ourPush;\n history.replaceState = ourReplace;\n window.addEventListener('popstate', fire);\n fire();\n return () => {\n stopped = true;\n window.removeEventListener('popstate', fire);\n // Restore only while OUR wrapper is still the outermost one. context.ts wraps on\n // first chat open, on top of us: assigning over it would drop its wrapper, and its\n // own restore would later hand ours back — which is what `stopped` is for.\n if (history.pushState === ourPush) history.pushState = origPush;\n if (history.replaceState === ourReplace) history.replaceState = origReplace;\n };\n}\n"]}
package/dist/embed.js CHANGED
@@ -1,2 +1,2 @@
1
- var y="mf-embed";function C(o,e){return{channel:y,protocol:3,...e?{id:e}:{},payload:o}}function M(o){return typeof o=="object"&&o!==null&&o.channel===y}var v=["tl","tr","bl","br"];function l(){return{mode:"dock",corner:"br",x:0,y:0,floatW:420,floatH:640,dockSide:"right",tabY:0,dockW:600}}function h(o,e,t){return Math.min(Math.max(o,e),t)}function R(o,e,t,r,n){return{x:o[1]==="l"?20:r-20-e,y:o[0]==="t"?20:n-20-t}}function u(o,e,t){return o.corner?R(o.corner,56,56,e,t):{x:h(o.x,20,Math.max(20,e-20-56)),y:h(o.y,20,Math.max(20,t-20-56))}}function c(o,e,t){if(o.corner)return o.corner;let r=u(o,e,t);return $(r.x+56/2,r.y+56/2,e,t)}function $(o,e,t,r){return`${e<r/2?"t":"b"}${o<t/2?"l":"r"}`}function V(o,e,t,r){let n=h(o,20,Math.max(20,t-20-56)),i=h(e,20,Math.max(20,r-20-56));for(let a of v){let d=R(a,56,56,t,r);if(Math.hypot(n-d.x,i-d.y)<=96)return{corner:a,x:n,y:i}}return{corner:null,x:n,y:i}}function x(o,e,t){return o<=64?"left":o+e>=t-64?"right":null}function S(o,e,t){return o.mode==="dock"?{x:o.dockSide==="left"?0:e-44,y:w(o,t)}:u(o,e,t)}function G(o,e,t,r){if(o.mode==="dock"){let a=e?Math.min(Math.max(o.dockW,320),Math.max(0,t-40)):44;return{x:o.dockSide==="left"?0:t-a,w:a}}let n=u(o,t,r);if(!e)return{x:n.x,w:56};let{w:i}=p(o.floatW,o.floatH,t,r,320,420);return{x:c(o,t,r)[1]==="r"?n.x+56-i:n.x,w:i}}function N(o,e,t,r,n,i){let a=V(o,e,n,i);if(a.corner)return{mode:"float",...a};let d=x(t,r,n);return d?{mode:"dock",dockSide:d,tabY:h(e,0,Math.max(0,i-132))}:{mode:"float",...a}}function p(o,e,t,r,n,i){return{w:Math.min(Math.max(o,n),Math.max(0,t-40)),h:Math.min(Math.max(e,i),Math.max(0,r-40))}}function T(o,e,t,r,n){let i=o[1]==="r"?-1:1,a=o[0]==="b"?-1:1;return{w:e+i*r,h:t+a*n}}function L(o,e,t){return o==="right"?e-t:e+t}function H(o){if(!o)return null;try{let e=JSON.parse(o);if(!e||e.mode!=="float"&&e.mode!=="dock")return null;let t=l(),r=(n,i)=>typeof n=="number"?n:i;return{mode:e.mode,corner:v.includes(e.corner)?e.corner:null,x:r(e.x,t.x),y:r(e.y,t.y),floatW:r(e.floatW,t.floatW),floatH:r(e.floatH,t.floatH),dockSide:e.dockSide==="left"?"left":"right",tabY:r(e.tabY,t.tabY),dockW:r(e.dockW,t.dockW)}}catch{return null}}function I(o){return JSON.stringify(o)}var s=o=>`${o}px`;function j(o,e,t,r,n,i,a){let d=n[1]==="r",b=n[0]==="b",K=d?i-(o+56):o,Y=b?a-(e+56):e,A=h(K,20,Math.max(20,i-20-t)),E=h(Y,20,Math.max(20,a-20-r));return{top:b?"auto":s(E),bottom:b?s(E):"auto",left:d?"auto":s(A),right:d?s(A):"auto",width:s(t),height:s(r)}}function w(o,e){let t=Math.max(0,e-132);return h(o.tabY||Math.round((e-132)/2),0,t)}function _(o,e,t){let r=o.dockSide==="left";return{top:s(w(o,t)),bottom:"auto",left:r?"0px":"auto",right:r?"auto":"0px",width:s(44),height:s(132)}}function f(o,e,t,r,n,i){let a=u(o,r,n);return j(a.x,a.y,e,t,i??c(o,r,n),r,n)}function W(o,e,t,r,n){let i=o.dockSide==="left";return{top:s(w(o,n)),bottom:"auto",left:i?"0px":"auto",right:i?"auto":"0px",width:s(e),height:s(t)}}function B(o,e,t,r){let n=p(o.floatW,o.floatH,e,t,320,420);return f(o,n.w,n.h,e,t,r)}function P(o,e,t){let r=Math.min(Math.max(o.dockW,320),Math.max(0,e-40)),n=o.dockSide==="left";return{top:"0px",bottom:"0px",height:"auto",left:n?"0px":"auto",right:n?"auto":"0px",width:s(r)}}var te="https://app.matterfact.com";function F(){try{if(new URLSearchParams(location.search).get("mfdev")==="1")return!0;try{return localStorage.getItem("mfdev")==="1"}catch{return!1}}catch{return!1}}function g(o,e){F()&&console.info("[embed auth] host: "+o,e??"")}function U(){let o=document.currentScript??document.querySelector('script[data-key][src*="embed"]'),e=o?.dataset.key;if(!e)return console.error("[matterfact] missing data-key on the embed script tag"),null;let t=o?.dataset.container,r=t?document.querySelector(t):null;t&&!r&&console.error(`[matterfact] data-container="${t}" matched nothing; falling back to the corner`);let n=o?.dataset.pageContext?.toLowerCase(),i=!0;return n==="off"||n==="false"||n==="0"?i=!1:n==="declared"?i="declared":(n==="full"||n==="on"||n==="true"||n==="1")&&(i=!0),{publishableKey:e,origin:o?.dataset.origin||te,theme:o?.dataset.theme||"auto",surface:o?.dataset.surface||"",container:r,pageContext:i}}var O="mf.embed.pos",k=class{constructor(e){this.config=e;this.iframe=null;this.shadow=null;this.queue=[];this.ready=!1;this.open=!1;this.geo=l();this.resizeBase=null;this.dockBase=600;this.dragFrom=null;this.dragAt={x:0,y:0};this.dragBox={x:0,w:0};this.dragGrowth=null;this.dragLeftBand=!1;this.context=null;this.hostEl=null;this.ac=0;this.onMessage=e=>{e.origin===this.config.origin&&e.source===this.iframe?.contentWindow&&M(e.data)&&this.handle(e.data.payload)};this.onViewportResize=()=>{this.place()};this.send=e=>{if(!this.ready){this.queue.push(e);return}this.iframe?.contentWindow?.postMessage(C(e),this.config.origin)};this.inline=!!e.container}mount(){let e=document.createElement("div");this.hostEl=e,e.id="matterfact-embed",this.inline?(e.style.cssText=["all: initial","position: relative","display: block","width: 100%","height: 100%","contain: layout style"].join(";"),this.config.container.appendChild(e)):(this.geo=this.readGeometry(),e.style.cssText=["all: initial","position: fixed","z-index: 2147483000","contain: layout style","transition: width .18s ease, height .18s ease, top .18s ease, right .18s ease, bottom .18s ease, left .18s ease"].join(";"),document.body.appendChild(e)),this.shadow=e.attachShadow({mode:"closed"});let t=document.createElement("style");t.textContent=":host{all:initial}iframe{border:0;display:block;width:100%;height:100%;background:transparent}"+(this.inline?"":':host([data-mode="float"]) iframe,:host([data-mode="dock"]) iframe{color-scheme:light dark}:host([data-mode="float"]) iframe{border-radius:12px;box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"]) iframe{box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"][data-flush="right"]) iframe{border-radius:12px 0 0 12px}:host([data-mode="dock"][data-flush="left"]) iframe{border-radius:0 12px 12px 0}'),this.shadow.appendChild(t);let r=document.createElement("iframe");r.title="matterfact assistant",r.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"),r.setAttribute("allow","microphone; clipboard-write"),r.src=`${this.config.origin}/embed/chat?k=${encodeURIComponent(this.config.publishableKey)}&o=${encodeURIComponent(location.origin)}`+(this.config.surface?`&s=${encodeURIComponent(this.config.surface)}`:"")+(F()||this.config.dev?"&dev=1":"")+(this.inline?"&inline=1":""),this.iframe=r,this.shadow.appendChild(r),window.addEventListener("message",this.onMessage),this.inline||(window.addEventListener("resize",this.onViewportResize),this.place())}__testHandle(e){this.handle(e)}emit(e){let t=globalThis.matterfact?.onEvent;if(typeof t=="function")try{t(e)}catch{}}handle(e){switch(e.type){case"widget.ready":this.ready=!0,this.send({type:"host.ready",protocol:3,origin:location.origin}),this.send({type:"host.theme",mode:this.themeMode()}),this.flush(),this.inline&&this.loadContext(),this.emit({type:"ready"});break;case"widget.setOpen":if(this.emit({type:e.open?"open":"close"}),this.inline)break;this.open=e.open,this.place(),e.open&&this.loadContext();break;case"widget.setMode":if(this.inline)break;this.geo.mode=e.mode,this.writeGeometry(this.geo),this.place();break;case"widget.setDockSide":if(this.inline)break;this.geo.dockSide=e.side,this.writeGeometry(this.geo),this.place();break;case"widget.snapCorner":if(this.inline)break;this.geo.corner=e.corner,this.writeGeometry(this.geo),this.place();break;case"widget.setLauncherRegion":{if(this.inline||this.open)break;let t=window.innerWidth,r=window.innerHeight;if(e.w<=56&&e.h<=56)this.place();else{let n=this.geo.mode==="dock"?W(this.geo,e.w,e.h,t,r):f(this.geo,e.w,e.h,t,r);this.applyBox(n,e.h>56?"menu":"pill")}break}case"widget.resize":if(this.inline)break;this.hostEl&&this.open&&this.geo.mode==="float"&&(this.hostEl.style.height=`${Math.min(e.height,Math.max(0,window.innerHeight-40))}px`);break;case"widget.resizeStart":if(this.inline)break;this.resizeBase={w:this.geo.floatW,h:this.geo.floatH},this.dockBase=this.geo.dockW,this.hostEl&&(this.hostEl.style.transition="none");break;case"widget.resizeMove":{if(this.inline||!this.resizeBase)break;if(this.geo.mode==="dock"){let t=L(this.geo.dockSide,this.dockBase,e.dx);this.geo.dockW=Math.min(Math.max(t,320),Math.max(0,window.innerWidth-40))}else{let t=T(c(this.geo,window.innerWidth,window.innerHeight),this.resizeBase.w,this.resizeBase.h,e.dx,e.dy),r=p(t.w,t.h,window.innerWidth,window.innerHeight,320,420);this.geo.floatW=r.w,this.geo.floatH=r.h}this.place();break}case"widget.resizeEnd":if(this.inline)break;this.resizeBase=null,this.hostEl&&(this.hostEl.style.transition=""),this.writeGeometry(this.geo);break;case"widget.requestContext":this.loadContext().then(t=>t.provideContext());break;case"widget.pageContextMax":this.loadContext().then(t=>{t.setPageContextMax(e.mode)});break;case"widget.requestSnapshot":this.loadContext().then(t=>t.sendSnapshot(this.send));break;case"widget.readRegion":this.loadContext().then(t=>t.sendRegion(e.ref,this.send));break;case"widget.callTool":this.loadContext().then(t=>t.callTool(e.call,this.send));break;case"widget.dragStart":if(this.inline)break;this.dragFrom=S(this.geo,window.innerWidth,window.innerHeight),this.dragAt={...this.dragFrom},this.dragLeftBand=!1,this.dragGrowth=c(this.geo,window.innerWidth,window.innerHeight),this.dragBox=G(this.geo,this.open,window.innerWidth,window.innerHeight),this.hostEl&&(this.hostEl.style.transition="none");break;case"widget.dragMove":{if(this.inline||!this.dragFrom)break;let t=window.innerWidth,r=window.innerHeight;this.dragAt={x:this.dragFrom.x+e.dx,y:this.dragFrom.y+e.dy};let n=x(this.dragBox.x+e.dx,this.dragBox.w,t);n||(this.dragLeftBand=!0),n&&(this.dragLeftBand||this.geo.mode==="dock")?(this.geo.mode="dock",this.geo.dockSide=n,this.geo.tabY=Math.max(0,this.dragAt.y)):(this.geo.mode="float",this.geo.corner=null,this.geo.x=this.dragAt.x,this.geo.y=this.dragAt.y),this.place();break}case"widget.dragEnd":{if(this.inline||!this.dragFrom)break;let t=N(this.dragAt.x,this.dragAt.y,this.dragBox.x+(this.dragAt.x-this.dragFrom.x),this.dragBox.w,window.innerWidth,window.innerHeight);this.geo.mode=t.mode,t.mode==="dock"?(this.geo.dockSide=t.dockSide,this.geo.tabY=t.tabY):(this.geo.corner=t.corner,this.geo.x=t.x,this.geo.y=t.y),this.dragFrom=null,this.dragGrowth=null,this.hostEl&&(this.hostEl.style.transition=""),this.place(),this.writeGeometry(this.geo);break}case"widget.resetPos":if(this.inline)break;this.geo=l(),this.writeGeometry(this.geo),this.place();break;case"widget.hide":if(this.inline)break;this.hostEl&&(this.hostEl.style.display="none");break;case"widget.needsAuth":this.emit({type:"auth",phase:"required"}),this.provideAuth();break;case"widget.navigate":this.emit({type:"navigate",href:e.href}),this.loadContext().then(t=>t.navigateHost(e.href));break;case"widget.chat":this.emit({type:"chat",phase:e.phase,chatId:e.chatId});break;case"widget.artifactParams":this.emit({type:"artifactParams",params:e.params,source:e.source});break}}applyBox(e,t){if(!this.hostEl||this.inline)return;let r=this.hostEl.style;r.top=e.top,r.right=e.right,r.bottom=e.bottom,r.left=e.left,r.width=e.width,r.height=e.height,this.hostEl.dataset.mode=t,t==="tab"||t==="dock"?this.hostEl.dataset.flush=this.geo.dockSide:delete this.hostEl.dataset.flush}growthNow(){return this.dragGrowth??c(this.geo,window.innerWidth,window.innerHeight)}place(){let e=window.innerWidth,t=window.innerHeight;if(this.open)this.geo.mode==="dock"?this.applyBox(P(this.geo,e,t),"dock"):this.applyBox(B(this.geo,e,t,this.growthNow()),"float");else{let r=this.geo.mode==="dock";this.applyBox(r?_(this.geo,e,t):f(this.geo,56,56,e,t,this.growthNow()),r?"tab":"launcher")}this.publishGeometry()}publishGeometry(){this.send({type:"host.geometry",mode:this.geo.mode,dockSide:this.geo.dockSide,growth:this.growthNow()})}themeMode(){return this.config.theme==="auto"?matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this.config.theme}readGeometry(){try{return H(localStorage.getItem(O))??l()}catch{return l()}}writeGeometry(e){try{localStorage.setItem(O,I(e))}catch{}}async provideAuth(){let e=this.config.authTokenProvider??globalThis.matterfact?.getEmbedAuthToken;if(!e){g("no provider");return}if(++this.ac>5){g("storm cap");return}try{let t=await e();t&&this.send({type:"host.auth",token:t,expiresAt:0}),this.emit({type:"auth",phase:t?"granted":"failed"}),g(t?"token":"empty")}catch(t){this.emit({type:"auth",phase:"failed"}),g("threw",t)}}loadContext(){return this.context??(this.context=import("./chunk-C7DXO37G.js").then(e=>(e.start(this.send,this.config.origin,this.config.pageContext,this.config.pageContextProvider),this.config.theme!=="auto"&&this.send({type:"host.theme",mode:this.config.theme}),e))),this.context}flush(){let e=this.queue;this.queue=[];for(let t of e)this.send(t)}setArtifactParams(e){if(e==null||typeof e!="object"||Array.isArray(e)){console.error("[matterfact] setArtifactParams expects an object of param values");return}this.send({type:"host.artifactParams",params:e})}destroy(){window.removeEventListener("message",this.onMessage),window.removeEventListener("resize",this.onViewportResize),this.hostEl?.remove(),this.hostEl=null,this.iframe=null,this.shadow=null,this.ready=!1,this.context?.then(e=>e.stop())}};function z(o){let e=new k(o);return e.mount(),window.matterfact={...window.matterfact??{},setArtifactParams:t=>e.setArtifactParams(t)},e}var q=U();if(q){let o=()=>z(q);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",o,{once:!0}):o()}
1
+ var y="mf-embed";function C(o,e){return{channel:y,protocol:4,...e?{id:e}:{},payload:o}}function M(o){return typeof o=="object"&&o!==null&&o.channel===y}var v=["tl","tr","bl","br"];function l(){return{mode:"dock",corner:"br",x:0,y:0,floatW:420,floatH:640,dockSide:"right",tabY:0,dockW:600}}function h(o,e,t){return Math.min(Math.max(o,e),t)}function R(o,e,t,r,n){return{x:o[1]==="l"?20:r-20-e,y:o[0]==="t"?20:n-20-t}}function p(o,e,t){return o.corner?R(o.corner,56,56,e,t):{x:h(o.x,20,Math.max(20,e-20-56)),y:h(o.y,20,Math.max(20,t-20-56))}}function c(o,e,t){if(o.corner)return o.corner;let r=p(o,e,t);return V(r.x+56/2,r.y+56/2,e,t)}function V(o,e,t,r){return`${e<r/2?"t":"b"}${o<t/2?"l":"r"}`}function $(o,e,t,r){let n=h(o,20,Math.max(20,t-20-56)),i=h(e,20,Math.max(20,r-20-56));for(let a of v){let s=R(a,56,56,t,r);if(Math.hypot(n-s.x,i-s.y)<=96)return{corner:a,x:n,y:i}}return{corner:null,x:n,y:i}}function x(o,e,t){return o<=64?"left":o+e>=t-64?"right":null}function S(o,e,t){return o.mode==="dock"?{x:o.dockSide==="left"?0:e-44,y:w(o,t)}:p(o,e,t)}function N(o,e,t,r){if(o.mode==="dock"){let a=e?Math.min(Math.max(o.dockW,320),Math.max(0,t-40)):44;return{x:o.dockSide==="left"?0:t-a,w:a}}let n=p(o,t,r);if(!e)return{x:n.x,w:56};let{w:i}=u(o.floatW,o.floatH,t,r,320,420);return{x:c(o,t,r)[1]==="r"?n.x+56-i:n.x,w:i}}function G(o,e,t,r,n,i){let a=$(o,e,n,i);if(a.corner)return{mode:"float",...a};let s=x(t,r,n);return s?{mode:"dock",dockSide:s,tabY:h(e,0,Math.max(0,i-132))}:{mode:"float",...a}}function u(o,e,t,r,n,i){return{w:Math.min(Math.max(o,n),Math.max(0,t-40)),h:Math.min(Math.max(e,i),Math.max(0,r-40))}}function T(o,e,t,r,n){let i=o[1]==="r"?-1:1,a=o[0]==="b"?-1:1;return{w:e+i*r,h:t+a*n}}function L(o,e,t){return o==="right"?e-t:e+t}function H(o){if(!o)return null;try{let e=JSON.parse(o);if(!e||e.mode!=="float"&&e.mode!=="dock")return null;let t=l(),r=(n,i)=>typeof n=="number"?n:i;return{mode:e.mode,corner:v.includes(e.corner)?e.corner:null,x:r(e.x,t.x),y:r(e.y,t.y),floatW:r(e.floatW,t.floatW),floatH:r(e.floatH,t.floatH),dockSide:e.dockSide==="left"?"left":"right",tabY:r(e.tabY,t.tabY),dockW:r(e.dockW,t.dockW)}}catch{return null}}function I(o){return JSON.stringify(o)}var d=o=>`${o}px`;function j(o,e,t,r,n,i,a){let s=n[1]==="r",b=n[0]==="b",K=s?i-(o+56):o,Y=b?a-(e+56):e,E=h(K,20,Math.max(20,i-20-t)),A=h(Y,20,Math.max(20,a-20-r));return{top:b?"auto":d(A),bottom:b?d(A):"auto",left:s?"auto":d(E),right:s?d(E):"auto",width:d(t),height:d(r)}}function w(o,e){let t=Math.max(0,e-132);return h(o.tabY||Math.round((e-132)/2),0,t)}function _(o,e,t){let r=o.dockSide==="left";return{top:d(w(o,t)),bottom:"auto",left:r?"0px":"auto",right:r?"auto":"0px",width:d(44),height:d(132)}}function f(o,e,t,r,n,i){let a=p(o,r,n);return j(a.x,a.y,e,t,i??c(o,r,n),r,n)}function W(o,e,t,r,n){let i=o.dockSide==="left";return{top:d(w(o,n)),bottom:"auto",left:i?"0px":"auto",right:i?"auto":"0px",width:d(e),height:d(t)}}function D(o,e,t,r){let n=u(o.floatW,o.floatH,e,t,320,420);return f(o,n.w,n.h,e,t,r)}function B(o,e,t){let r=Math.min(Math.max(o.dockW,320),Math.max(0,e-40)),n=o.dockSide==="left";return{top:"0px",bottom:"0px",height:"auto",left:n?"0px":"auto",right:n?"auto":"0px",width:d(r)}}var te="https://app.matterfact.com",oe="0.18.0";function F(){try{if(new URLSearchParams(location.search).get("mfdev")==="1")return!0;try{return localStorage.getItem("mfdev")==="1"}catch{return!1}}catch{return!1}}function g(o,e){F()&&console.info("[embed auth] host: "+o,e??"")}function U(){let o=document.currentScript??document.querySelector('script[data-key][src*="embed"]'),e=o?.dataset.key;if(!e)return console.error("[matterfact] missing data-key on the embed script tag"),null;let t=o?.dataset.container,r=t?document.querySelector(t):null;t&&!r&&console.error(`[matterfact] data-container="${t}" matched nothing; falling back to the corner`);let n=o?.dataset.pageContext?.toLowerCase(),i=!0;return n==="off"||n==="false"||n==="0"?i=!1:n==="declared"?i="declared":(n==="full"||n==="on"||n==="true"||n==="1")&&(i=!0),{publishableKey:e,origin:o?.dataset.origin||te,theme:o?.dataset.theme||"auto",surface:o?.dataset.surface||"",container:r,pageContext:i,shareDeeplinkFormat:o?.dataset.shareDeeplinkFormat}}var O="mf.embed.pos",k=class{constructor(e){this.config=e;this.iframe=null;this.shadow=null;this.queue=[];this.ready=!1;this.open=!1;this.geo=l();this.resizeBase=null;this.dockBase=600;this.dragFrom=null;this.dragAt={x:0,y:0};this.dragBox={x:0,w:0};this.dragGrowth=null;this.dragLeftBand=!1;this.context=null;this.hostEl=null;this.ac=0;this.onMessage=e=>{e.origin===this.config.origin&&e.source===this.iframe?.contentWindow&&M(e.data)&&this.handle(e.data.payload)};this.onViewportResize=()=>{this.place()};this.send=e=>{if(!this.ready){this.queue.push(e);return}this.iframe?.contentWindow?.postMessage(C(e),this.config.origin)};this.inline=!!e.container}mount(){let e=window;(e.matterfact??(e.matterfact={})).embedVersion=oe;let t=document.createElement("div");this.hostEl=t,t.id="matterfact-embed",this.inline?(t.style.cssText=["all: initial","position: relative","display: block","width: 100%","height: 100%","contain: layout style"].join(";"),this.config.container.appendChild(t)):(this.geo=this.readGeometry(),t.style.cssText=["all: initial","position: fixed","z-index: 2147483000","contain: layout style","transition: width .18s ease, height .18s ease, top .18s ease, right .18s ease, bottom .18s ease, left .18s ease"].join(";"),document.body.appendChild(t)),this.shadow=t.attachShadow({mode:"closed"});let r=document.createElement("style");r.textContent=":host{all:initial}iframe{border:0;display:block;width:100%;height:100%;background:transparent}"+(this.inline?"":':host([data-mode="float"]) iframe,:host([data-mode="dock"]) iframe{color-scheme:light dark}:host([data-mode="float"]) iframe{border-radius:12px;box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"]) iframe{box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"][data-flush="right"]) iframe{border-radius:12px 0 0 12px}:host([data-mode="dock"][data-flush="left"]) iframe{border-radius:0 12px 12px 0}'),this.shadow.appendChild(r);let n=document.createElement("iframe");n.title="matterfact assistant",n.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"),n.setAttribute("allow","microphone; clipboard-write; web-share"),n.src=`${this.config.origin}/embed/chat?k=${encodeURIComponent(this.config.publishableKey)}&o=${encodeURIComponent(location.origin)}`+(this.config.surface?`&s=${encodeURIComponent(this.config.surface)}`:"")+(F()||this.config.dev?"&dev=1":"")+(this.inline?"&inline=1":""),this.iframe=n,this.shadow.appendChild(n),window.addEventListener("message",this.onMessage),this.inline||(window.addEventListener("resize",this.onViewportResize),this.place());let i=this.config.shareDeeplinkFormat;i&&import("./chunk-5X2WFSIL.js").then(a=>{this.destroyed||(this.deeplinkStop=a.installDeeplinkWatch(i,s=>this.send({type:"host.openShare",token:s}),s=>this.send({type:"host.deeplinkFormat",format:s})))}).catch(()=>{})}__testHandle(e){this.handle(e)}emit(e){let t=globalThis.matterfact?.onEvent;if(typeof t=="function")try{t(e)}catch{}}handle(e){switch(e.type){case"widget.ready":this.ready=!0,this.send({type:"host.ready",protocol:4,origin:location.origin}),this.send({type:"host.theme",mode:this.themeMode()}),this.flush(),this.inline&&this.loadContext(),this.emit({type:"ready"});break;case"widget.setOpen":if(this.emit({type:e.open?"open":"close"}),this.inline)break;this.open=e.open,this.place(),e.open&&this.loadContext();break;case"widget.setMode":if(this.inline)break;this.geo.mode=e.mode,this.writeGeometry(this.geo),this.place();break;case"widget.setDockSide":if(this.inline)break;this.geo.dockSide=e.side,this.writeGeometry(this.geo),this.place();break;case"widget.snapCorner":if(this.inline)break;this.geo.corner=e.corner,this.writeGeometry(this.geo),this.place();break;case"widget.setLauncherRegion":{if(this.inline||this.open)break;let t=window.innerWidth,r=window.innerHeight;if(e.w<=56&&e.h<=56)this.place();else{let n=this.geo.mode==="dock"?W(this.geo,e.w,e.h,t,r):f(this.geo,e.w,e.h,t,r);this.applyBox(n,e.h>56?"menu":"pill")}break}case"widget.resize":if(this.inline)break;this.hostEl&&this.open&&this.geo.mode==="float"&&(this.hostEl.style.height=`${Math.min(e.height,Math.max(0,window.innerHeight-40))}px`);break;case"widget.resizeStart":if(this.inline)break;this.resizeBase={w:this.geo.floatW,h:this.geo.floatH},this.dockBase=this.geo.dockW,this.hostEl&&(this.hostEl.style.transition="none");break;case"widget.resizeMove":{if(this.inline||!this.resizeBase)break;if(this.geo.mode==="dock"){let t=L(this.geo.dockSide,this.dockBase,e.dx);this.geo.dockW=Math.min(Math.max(t,320),Math.max(0,window.innerWidth-40))}else{let t=T(c(this.geo,window.innerWidth,window.innerHeight),this.resizeBase.w,this.resizeBase.h,e.dx,e.dy),r=u(t.w,t.h,window.innerWidth,window.innerHeight,320,420);this.geo.floatW=r.w,this.geo.floatH=r.h}this.place();break}case"widget.resizeEnd":if(this.inline)break;this.resizeBase=null,this.hostEl&&(this.hostEl.style.transition=""),this.writeGeometry(this.geo);break;case"widget.requestContext":this.loadContext().then(t=>t.provideContext());break;case"widget.pageContextMax":this.loadContext().then(t=>{t.setPageContextMax(e.mode)});break;case"widget.requestSnapshot":this.loadContext().then(t=>t.sendSnapshot(this.send));break;case"widget.readRegion":this.loadContext().then(t=>t.sendRegion(e.ref,this.send));break;case"widget.callTool":this.loadContext().then(t=>t.callTool(e.call,this.send));break;case"widget.dragStart":if(this.inline)break;this.dragFrom=S(this.geo,window.innerWidth,window.innerHeight),this.dragAt={...this.dragFrom},this.dragLeftBand=!1,this.dragGrowth=c(this.geo,window.innerWidth,window.innerHeight),this.dragBox=N(this.geo,this.open,window.innerWidth,window.innerHeight),this.hostEl&&(this.hostEl.style.transition="none");break;case"widget.dragMove":{if(this.inline||!this.dragFrom)break;let t=window.innerWidth,r=window.innerHeight;this.dragAt={x:this.dragFrom.x+e.dx,y:this.dragFrom.y+e.dy};let n=x(this.dragBox.x+e.dx,this.dragBox.w,t);n||(this.dragLeftBand=!0),n&&(this.dragLeftBand||this.geo.mode==="dock")?(this.geo.mode="dock",this.geo.dockSide=n,this.geo.tabY=Math.max(0,this.dragAt.y)):(this.geo.mode="float",this.geo.corner=null,this.geo.x=this.dragAt.x,this.geo.y=this.dragAt.y),this.place();break}case"widget.dragEnd":{if(this.inline||!this.dragFrom)break;let t=G(this.dragAt.x,this.dragAt.y,this.dragBox.x+(this.dragAt.x-this.dragFrom.x),this.dragBox.w,window.innerWidth,window.innerHeight);this.geo.mode=t.mode,t.mode==="dock"?(this.geo.dockSide=t.dockSide,this.geo.tabY=t.tabY):(this.geo.corner=t.corner,this.geo.x=t.x,this.geo.y=t.y),this.dragFrom=null,this.dragGrowth=null,this.hostEl&&(this.hostEl.style.transition=""),this.place(),this.writeGeometry(this.geo);break}case"widget.resetPos":if(this.inline)break;this.geo=l(),this.writeGeometry(this.geo),this.place();break;case"widget.hide":if(this.inline)break;this.hostEl&&(this.hostEl.style.display="none");break;case"widget.needsAuth":this.emit({type:"auth",phase:"required"}),this.provideAuth();break;case"widget.navigate":this.emit({type:"navigate",href:e.href}),this.loadContext().then(t=>t.navigateHost(e.href));break;case"widget.chat":this.emit({type:"chat",phase:e.phase,chatId:e.chatId});break;case"widget.artifactParams":this.emit({type:"artifactParams",params:e.params,source:e.source});break}}applyBox(e,t){if(!this.hostEl||this.inline)return;let r=this.hostEl.style;r.top=e.top,r.right=e.right,r.bottom=e.bottom,r.left=e.left,r.width=e.width,r.height=e.height,this.hostEl.dataset.mode=t,t==="tab"||t==="dock"?this.hostEl.dataset.flush=this.geo.dockSide:delete this.hostEl.dataset.flush}growthNow(){return this.dragGrowth??c(this.geo,window.innerWidth,window.innerHeight)}place(){let e=window.innerWidth,t=window.innerHeight;if(this.open)this.geo.mode==="dock"?this.applyBox(B(this.geo,e,t),"dock"):this.applyBox(D(this.geo,e,t,this.growthNow()),"float");else{let r=this.geo.mode==="dock";this.applyBox(r?_(this.geo,e,t):f(this.geo,56,56,e,t,this.growthNow()),r?"tab":"launcher")}this.publishGeometry()}publishGeometry(){this.send({type:"host.geometry",mode:this.geo.mode,dockSide:this.geo.dockSide,growth:this.growthNow()})}themeMode(){return this.config.theme==="auto"?matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this.config.theme}readGeometry(){try{return H(localStorage.getItem(O))??l()}catch{return l()}}writeGeometry(e){try{localStorage.setItem(O,I(e))}catch{}}async provideAuth(){let e=this.config.authTokenProvider??globalThis.matterfact?.getEmbedAuthToken;if(!e){g("no provider");return}if(++this.ac>5){g("storm cap");return}try{let t=await e();t&&this.send({type:"host.auth",token:t,expiresAt:0}),this.emit({type:"auth",phase:t?"granted":"failed"}),g(t?"token":"empty")}catch(t){this.emit({type:"auth",phase:"failed"}),g("threw",t)}}loadContext(){return this.context??(this.context=import("./chunk-C7DXO37G.js").then(e=>(e.start(this.send,this.config.origin,this.config.pageContext,this.config.pageContextProvider),this.config.theme!=="auto"&&this.send({type:"host.theme",mode:this.config.theme}),e))),this.context}flush(){let e=this.queue;this.queue=[];for(let t of e)this.send(t)}setArtifactParams(e){if(e==null||typeof e!="object"||Array.isArray(e)){console.error("[matterfact] setArtifactParams expects an object of param values");return}this.send({type:"host.artifactParams",params:e})}destroy(){window.removeEventListener("message",this.onMessage),window.removeEventListener("resize",this.onViewportResize),this.hostEl?.remove(),this.hostEl=null,this.iframe=null,this.shadow=null,this.ready=!1,this.destroyed=!0,this.deeplinkStop?.(),this.deeplinkStop=null,this.context?.then(e=>e.stop())}};function z(o){let e=new k(o);return e.mount(),window.matterfact={...window.matterfact??{},setArtifactParams:t=>e.setArtifactParams(t)},e}var q=U();if(q){let o=()=>z(q);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",o,{once:!0}):o()}
2
2
  //# sourceMappingURL=embed.js.map