@patterninc/pattern-ui 0.0.0-main.202610210601.309a97ae982a → 0.0.0-main.202610210708.fb02fcd2f652
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/@patterninc/pattern-ui/contrast.js +9 -9
- package/dist/browser/@patterninc/pattern-ui/density-context.js +25 -0
- package/dist/browser/@patterninc/pattern-ui/theme.js +1 -1
- package/dist/browser/@patterninc/pattern-ui/tone.js +21 -0
- package/dist/browser/_chunks/root-setting-CrLE5SPb.js +1 -0
- package/dist/play-cdn-claude.js +1 -1
- package/dist/utils/contrast-context.js +46 -55
- package/dist/utils/density-context.js +209 -0
- package/dist/utils/root-setting.js +61 -0
- package/dist/utils/theme-context.js +99 -11
- package/dist/utils/tone-context.js +203 -0
- package/package.json +9 -1
- package/src/utils/contrast-context.tsx +21 -18
- package/src/utils/density-context.tsx +197 -0
- package/src/utils/root-setting.ts +70 -0
- package/src/utils/theme-context.tsx +81 -12
- package/src/utils/tone-context.tsx +201 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{
|
|
1
|
+
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{n as i,t as a}from"../../_chunks/root-setting-CrLE5SPb.js";import{a as o}from"../../_chunks/user-settings-Do1mOxtc.js";var s=n(),c=/* @__PURE__ */ e(r(),1),l=/* @__PURE__ */ e(t(),1);const u=`contrast`,d=[`default`,`high`],f=(0,l.createContext)(void 0);function p(e){return d.find(t=>t===e)??null}function m(e,t=typeof document>`u`?void 0:document.documentElement){t&&(e===`high`?t.dataset.contrast=`high`:delete t.dataset.contrast)}function h(){if(typeof window>`u`)return null;try{let e=window.localStorage.getItem(u);return e?p(JSON.parse(e)):null}catch{return null}}function g(e){if(typeof window<`u`)try{window.localStorage.setItem(u,JSON.stringify(e))}catch{}}function _(e,{gateStorageKey:t}={}){let n=JSON.stringify(u),r=JSON.stringify(d),i=JSON.stringify(`default`);return`;(() => {
|
|
2
2
|
const root = document.documentElement
|
|
3
|
-
const allowed = ${
|
|
3
|
+
const allowed = ${r}
|
|
4
4
|
const initial = ${JSON.stringify(e??null)}
|
|
5
|
-
let contrast = ${
|
|
5
|
+
let contrast = ${i}
|
|
6
6
|
try {
|
|
7
|
-
if (allowed.includes(initial)) {
|
|
7
|
+
${a(t)}if (allowed.includes(initial)) {
|
|
8
8
|
contrast = initial
|
|
9
|
-
localStorage.setItem(${
|
|
9
|
+
localStorage.setItem(${n}, JSON.stringify(initial))
|
|
10
10
|
} else {
|
|
11
|
-
const stored = localStorage.getItem(${
|
|
12
|
-
const parsed = stored ? JSON.parse(stored) : ${
|
|
13
|
-
contrast = allowed.includes(parsed) ? parsed : ${
|
|
11
|
+
const stored = localStorage.getItem(${n})
|
|
12
|
+
const parsed = stored ? JSON.parse(stored) : ${i}
|
|
13
|
+
contrast = allowed.includes(parsed) ? parsed : ${i}
|
|
14
14
|
}
|
|
15
15
|
} catch {}
|
|
16
16
|
if (contrast === 'high') {
|
|
@@ -18,4 +18,4 @@ import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../
|
|
|
18
18
|
} else {
|
|
19
19
|
delete root.dataset.contrast
|
|
20
20
|
}
|
|
21
|
-
})()`}function
|
|
21
|
+
})()`}function v(e){let t=(0,s.c)(12),{children:n,enabled:r,initialContrast:a,gateStorageKey:o}=e,l=r===void 0||r,u;t[0]!==l||t[1]!==o||t[2]!==a?(u={enabled:l,initial:a,fallback:`default`,readStored:h,apply:m,gateStorageKey:o},t[0]=l,t[1]=o,t[2]=a,t[3]=u):u=t[3];let[d,p]=i(u),_=l?d:`default`,v;t[4]===p?v=t[5]:(v=e=>{g(e),p(e)},t[4]=p,t[5]=v);let y=v,b;t[6]!==_||t[7]!==y?(b={contrast:_,setContrast:y},t[6]=_,t[7]=y,t[8]=b):b=t[8];let x=b,S;return t[9]!==n||t[10]!==x?(S=/* @__PURE__ */ (0,c.jsx)(f.Provider,{value:x,children:n}),t[9]=n,t[10]=x,t[11]=S):S=t[11],S}function y(){let e=(0,l.useContext)(f);if(e===void 0)throw Error(`useContrast must be used within a ContrastProvider`);return e}const b=`settings:contrast`;function x(e){return p(typeof e==`object`&&e?e.contrast:e)}function S(e){let t=(0,s.c)(11),{client:n,enabled:r}=e,i=r===void 0||r,{contrast:a,setContrast:c}=y(),l;t[0]!==n||t[1]!==a||t[2]!==i||t[3]!==c?(l={client:n,key:b,enabled:i,value:a,readExplicitValue:h,parse:x,serialize:C,onRemoteValue:c},t[0]=n,t[1]=a,t[2]=i,t[3]=c,t[4]=l):l=t[4];let u=o(l),d;t[5]!==u||t[6]!==c?(d=e=>{c(e),u(e)},t[5]=u,t[6]=c,t[7]=d):d=t[7];let f;return t[8]!==a||t[9]!==d?(f={contrast:a,setContrast:d},t[8]=a,t[9]=d,t[10]=f):f=t[10],f}function C(e){return{contrast:e}}export{d as CONTRAST_PREFERENCES,b as CONTRAST_SETTING_KEY,u as CONTRAST_STORAGE_KEY,f as ContrastContext,v as ContrastProvider,m as applyContrastAttribute,_ as getContrastNoFlashScript,p as parseContrastPreference,x as parseContrastSetting,h as readExplicitContrast,S as useAccountContrast,y as useContrast};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{a as i,f as a,i as o,o as s}from"../../_chunks/density-Cp1PW4SA.js";import{n as c,t as l}from"../../_chunks/root-setting-CrLE5SPb.js";import{a as u}from"../../_chunks/user-settings-Do1mOxtc.js";var d=n(),f=/* @__PURE__ */ e(r(),1),p=/* @__PURE__ */ e(t(),1);const m=`density`,h=`settings:density`,g={available:!1,density:1,setDensity:()=>void 0},_=(0,p.createContext)(g);function v(e){let t=typeof e==`object`&&e?e.density:e,n=typeof t==`string`?Number.parseFloat(t):t;return typeof n!=`number`||!Number.isFinite(n)?null:a(n)}function y(e){typeof document<`u`&&s(document.documentElement,e)}function b(){if(typeof window>`u`)return null;try{return v(window.localStorage.getItem(m))}catch{return null}}function x(e){if(typeof window<`u`)try{window.localStorage.setItem(m,String(e))}catch{}}function S(e,{gateStorageKey:t}={}){let n=JSON.stringify(m),r=JSON.stringify(e??null),a=JSON.stringify(Object.fromEntries(i.map(e=>[e.value,e.name])));return`;(() => {
|
|
2
|
+
try {
|
|
3
|
+
${l(t)}const initial = ${r}
|
|
4
|
+
if (typeof initial === 'number') localStorage.setItem(${n}, String(initial))
|
|
5
|
+
const stored = typeof initial === 'number' ? initial : parseFloat(localStorage.getItem(${n}) || '1')
|
|
6
|
+
const raw = Math.min(1.5, Math.max(0.75, stored || 1))
|
|
7
|
+
const steps = ${JSON.stringify(o)}
|
|
8
|
+
let d = 1
|
|
9
|
+
for (const step of steps) {
|
|
10
|
+
const gap = Math.abs(step - raw) - Math.abs(d - raw)
|
|
11
|
+
if (gap < -1e-9 || (Math.abs(gap) <= 1e-9 && Math.abs(step - 1) < Math.abs(d - 1))) d = step
|
|
12
|
+
}
|
|
13
|
+
const root = document.documentElement
|
|
14
|
+
const stop = ${a}[String(d)]
|
|
15
|
+
root.style.removeProperty('--pui-density')
|
|
16
|
+
if (d === 1) {
|
|
17
|
+
root.removeAttribute('data-density')
|
|
18
|
+
} else if (stop) {
|
|
19
|
+
root.setAttribute('data-density', stop)
|
|
20
|
+
} else {
|
|
21
|
+
root.setAttribute('data-density', '')
|
|
22
|
+
root.style.setProperty('--pui-density', String(d))
|
|
23
|
+
}
|
|
24
|
+
} catch {}
|
|
25
|
+
})()`}function C(e){let t=(0,d.c)(13),{children:n,enabled:r,initialDensity:i,gateStorageKey:o}=e,s=r===void 0||r,l;t[0]===i?l=t[1]:(l=i===void 0?void 0:a(i),t[0]=i,t[1]=l);let u;t[2]!==s||t[3]!==o||t[4]!==l?(u={enabled:s,initial:l,fallback:1,readStored:b,apply:y,gateStorageKey:o},t[2]=s,t[3]=o,t[4]=l,t[5]=u):u=t[5];let[p,m]=c(u),h=s?p:1,v;t[6]!==h||t[7]!==s||t[8]!==m?(v=s?{available:!0,density:h,setDensity:e=>{let t=a(e);x(t),m(t)}}:g,t[6]=h,t[7]=s,t[8]=m,t[9]=v):v=t[9];let S=v,C;return t[10]!==n||t[11]!==S?(C=/* @__PURE__ */ (0,f.jsx)(_.Provider,{value:S,children:n}),t[10]=n,t[11]=S,t[12]=C):C=t[12],C}function w(){return(0,p.useContext)(_)}function T(e){let t=(0,d.c)(12),{client:n,enabled:r}=e,i=r===void 0||r,{available:o,density:s,setDensity:c}=w(),l=i&&o,f;t[0]!==n||t[1]!==s||t[2]!==c||t[3]!==l?(f={client:n,key:h,enabled:l,value:s,readExplicitValue:b,parse:v,serialize:E,onRemoteValue:c},t[0]=n,t[1]=s,t[2]=c,t[3]=l,t[4]=f):f=t[4];let p=u(f),m;t[5]!==p||t[6]!==c?(m=e=>{let t=a(e);c(t),p(t)},t[5]=p,t[6]=c,t[7]=m):m=t[7];let g;return t[8]!==o||t[9]!==s||t[10]!==m?(g={available:o,density:s,setDensity:m},t[8]=o,t[9]=s,t[10]=m,t[11]=g):g=t[11],g}function E(e){return{density:e}}export{h as DENSITY_SETTING_KEY,m as DENSITY_STORAGE_KEY,_ as DensityContext,C as DensityProvider,S as getDensityNoFlashScript,v as parseDensitySetting,b as readExplicitDensity,T as useAccountDensity,w as useDensity};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{l as i}from"../../_chunks/dist-CPlSvWC_.js";import{n as
|
|
1
|
+
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{l as i}from"../../_chunks/dist-CPlSvWC_.js";import{a}from"../../_chunks/user-settings-Do1mOxtc.js";import{n as o}from"../../_chunks/theme-transition-D3CJt3SA.js";var s=n(),c=/* @__PURE__ */ e(r(),1),l=/* @__PURE__ */ e(t(),1);const u=[`light`,`dark`,`system`],d=`settings:theme`,f=(0,l.createContext)(void 0);function p(e){if(typeof document>`u`)return;let t=document.documentElement;e?t.classList.add(`dark`):t.classList.remove(`dark`)}function m(){return b()??`system`}function h(e){return typeof window>`u`?`light`:e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`:e}function g(e){let t=(0,s.c)(13),{children:n}=e,r;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(r={defaultValue:`system`,localStorageKey:`theme`,initializeWithValue:!1},t[0]=r):r=t[0];let{ternaryDarkMode:a,setTernaryDarkMode:u,isDarkMode:d}=i(r),g=a,_=d?`dark`:`light`,v=(0,l.useRef)(!1),y,b;t[1]===d?(y=t[2],b=t[3]):(y=()=>{v.current?p(d):(v.current=!0,p(h(m())===`dark`))},b=[d],t[1]=d,t[2]=y,t[3]=b),(0,l.useEffect)(y,b);let x;t[4]===u?x=t[5]:(x=(e,t)=>{let{transition:n}=t===void 0?{}:t,r=n===void 0||n,i=()=>{p(h(e)===`dark`),u(e)};r?o(i):i()},t[4]=u,t[5]=x);let S=x,C;t[6]!==_||t[7]!==S||t[8]!==g?(C={theme:g,effectiveTheme:_,setTheme:S},t[6]=_,t[7]=S,t[8]=g,t[9]=C):C=t[9];let w=C,T;return t[10]!==n||t[11]!==w?(T=/* @__PURE__ */ (0,c.jsx)(f.Provider,{value:w,children:n}),t[10]=n,t[11]=w,t[12]=T):T=t[12],T}function _(){let e=(0,l.useContext)(f);if(e===void 0)throw Error(`useTheme must be used within a ThemeProvider`);return e}function v(e){return u.find(t=>t===e)??null}function y(e){return v(typeof e==`object`&&e?e.theme:e)}function b(){if(typeof window>`u`)return null;try{let e=window.localStorage.getItem(`theme`);return e?v(JSON.parse(e)):null}catch{return null}}function x(e){let t=(0,s.c)(14),{client:n,enabled:r}=e,i=r===void 0||r,{theme:o,effectiveTheme:c,setTheme:l}=_(),u;t[0]===l?u=t[1]:(u=e=>l(e,{transition:!1}),t[0]=l,t[1]=u);let f;t[2]!==n||t[3]!==i||t[4]!==u||t[5]!==o?(f={client:n,key:d,enabled:i,value:o,readExplicitValue:b,parse:y,serialize:S,onRemoteValue:u},t[2]=n,t[3]=i,t[4]=u,t[5]=o,t[6]=f):f=t[6];let p=a(f),m;t[7]!==p||t[8]!==l?(m=(e,t)=>{l(e,t),p(e)},t[7]=p,t[8]=l,t[9]=m):m=t[9];let h;return t[10]!==c||t[11]!==m||t[12]!==o?(h={theme:o,effectiveTheme:c,setTheme:m},t[10]=c,t[11]=m,t[12]=o,t[13]=h):h=t[13],h}function S(e){return{theme:e}}export{u as THEMES,d as THEME_SETTING_KEY,f as ThemeContext,g as ThemeProvider,v as parseTheme,y as parseThemeSetting,b as readExplicitTheme,x as useAccountTheme,_ as useTheme};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{a as e}from"../../_chunks/rolldown-runtime-BWaJ3C7D.js";import{t}from"../../_chunks/react-CUaU2Z9V.js";import{t as n}from"../../_chunks/compiler-runtime-B3BlmaWz.js";import{t as r}from"../../_chunks/jsx-runtime-Dgn0yPdi.js";import{n as i,t as a}from"../../_chunks/root-setting-CrLE5SPb.js";import{a as o}from"../../_chunks/user-settings-Do1mOxtc.js";var s=n(),c=/* @__PURE__ */ e(r(),1),l=/* @__PURE__ */ e(t(),1);const u=`tone`,d=[`default`,`warm`,`cool`],f=`settings:tone`,p={available:!1,tone:`default`,setTone:()=>void 0},m=(0,l.createContext)(p);function h(e){return d.find(t=>t===e)??null}function g(e){return h(typeof e==`object`&&e?e.tone:e)}function _(e,t=typeof document>`u`?void 0:document.documentElement){t&&(e==="default"?delete t.dataset.theme:t.dataset.theme=e)}function v(){if(typeof window>`u`)return null;try{let e=window.localStorage.getItem(u);return e?h(JSON.parse(e)):null}catch{return null}}function y(e){if(typeof window<`u`)try{window.localStorage.setItem(u,JSON.stringify(e))}catch{}}function b(e,{gateStorageKey:t}={}){let n=JSON.stringify(u);return`;(() => {
|
|
2
|
+
const root = document.documentElement
|
|
3
|
+
const allowed = ${JSON.stringify(d)}
|
|
4
|
+
const initial = ${JSON.stringify(e??null)}
|
|
5
|
+
let tone = 'default'
|
|
6
|
+
try {
|
|
7
|
+
${a(t)}if (allowed.includes(initial)) {
|
|
8
|
+
tone = initial
|
|
9
|
+
localStorage.setItem(${n}, JSON.stringify(initial))
|
|
10
|
+
} else {
|
|
11
|
+
const stored = localStorage.getItem(${n})
|
|
12
|
+
const parsed = stored ? JSON.parse(stored) : 'default'
|
|
13
|
+
tone = allowed.includes(parsed) ? parsed : 'default'
|
|
14
|
+
}
|
|
15
|
+
} catch {}
|
|
16
|
+
if (tone === 'default') {
|
|
17
|
+
delete root.dataset.theme
|
|
18
|
+
} else {
|
|
19
|
+
root.dataset.theme = tone
|
|
20
|
+
}
|
|
21
|
+
})()`}function x(e){let t=(0,s.c)(11),{children:n,enabled:r,initialTone:a,gateStorageKey:o}=e,l=r===void 0||r,u;t[0]!==l||t[1]!==o||t[2]!==a?(u={enabled:l,initial:a,fallback:`default`,readStored:v,apply:_,gateStorageKey:o},t[0]=l,t[1]=o,t[2]=a,t[3]=u):u=t[3];let[d,f]=i(u),h=l?d:`default`,g;t[4]!==l||t[5]!==f||t[6]!==h?(g=l?{available:!0,tone:h,setTone:e=>{y(e),f(e)}}:p,t[4]=l,t[5]=f,t[6]=h,t[7]=g):g=t[7];let b=g,x;return t[8]!==n||t[9]!==b?(x=/* @__PURE__ */ (0,c.jsx)(m.Provider,{value:b,children:n}),t[8]=n,t[9]=b,t[10]=x):x=t[10],x}function S(){return(0,l.useContext)(m)}function C(e){let t=(0,s.c)(12),{client:n,enabled:r}=e,i=r===void 0||r,{available:a,tone:c,setTone:l}=S(),u=i&&a,d;t[0]!==n||t[1]!==l||t[2]!==u||t[3]!==c?(d={client:n,key:f,enabled:u,value:c,readExplicitValue:v,parse:g,serialize:w,onRemoteValue:l},t[0]=n,t[1]=l,t[2]=u,t[3]=c,t[4]=d):d=t[4];let p=o(d),m;t[5]!==p||t[6]!==l?(m=e=>{l(e),p(e)},t[5]=p,t[6]=l,t[7]=m):m=t[7];let h;return t[8]!==a||t[9]!==m||t[10]!==c?(h={available:a,tone:c,setTone:m},t[8]=a,t[9]=m,t[10]=c,t[11]=h):h=t[11],h}function w(e){return{tone:e}}export{d as TONES,f as TONE_SETTING_KEY,u as TONE_STORAGE_KEY,m as ToneContext,x as ToneProvider,_ as applyTone,b as getToneNoFlashScript,h as parseTone,g as parseToneSetting,v as readExplicitTone,C as useAccountTone,S as useTone};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-BWaJ3C7D.js";import{t}from"./react-CUaU2Z9V.js";var n=/* @__PURE__ */ e(t(),1);function r(e,t){try{window.localStorage.setItem(e,String(t))}catch{}}function i({enabled:e,initial:t,fallback:i,readStored:a,apply:o,gateStorageKey:s}){let[c,l]=(0,n.useState)(t??i),[u,d]=(0,n.useState)(t!==void 0),f=(0,n.useRef)(!1);return(0,n.useEffect)(()=>{if(u)return;let e=a();e!==null&&l(e),d(!0)},[u,a]),(0,n.useEffect)(()=>{u&&(s&&r(s,e),e?(f.current=!0,o(c)):((f.current||a()!==null)&&o(i),f.current=!1))},[u,e,c,i,a,o,s]),[c,l]}function a(e){return e?`if (localStorage.getItem(${JSON.stringify(e)}) !== 'true') return\n `:``}export{i as n,a as t};
|
package/dist/play-cdn-claude.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const NPM_CDN = "https://cdn.jsdelivr.net/npm/";
|
|
3
3
|
const SHARED = /^(@patterninc\/pattern-ui\/|(react|react-dom|@lingui\/core|@lingui\/react|sucrase)(\/|$))/;
|
|
4
4
|
const scriptUrl = document.currentScript instanceof HTMLScriptElement ? document.currentScript.src : "";
|
|
5
|
-
const packageUrl = new URL("../", scriptUrl || location.href).href.replace(/\/@patterninc\/pattern-ui(@[^/]*)?\//, `/@patterninc/pattern-ui@0.0.0-main.
|
|
5
|
+
const packageUrl = new URL("../", scriptUrl || location.href).href.replace(/\/@patterninc\/pattern-ui(@[^/]*)?\//, `/@patterninc/pattern-ui@0.0.0-main.202610210708.fb02fcd2f652/`);
|
|
6
6
|
globalThis.patternPlayCdnUrl = (specifier) => {
|
|
7
7
|
if (SHARED.test(specifier)) return `${packageUrl}dist/browser/${specifier}.js`;
|
|
8
8
|
return specifier.endsWith(".css") ? `${NPM_CDN}${specifier}` : `${NPM_CDN}${specifier}/+esm`;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { gateScriptGuard, useRootSetting } from "./root-setting.js";
|
|
1
2
|
import { useAccountSetting } from "./user-settings.js";
|
|
2
3
|
import { c } from "react/compiler-runtime";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import React, { createContext, useContext
|
|
5
|
+
import React, { createContext, useContext } from "react";
|
|
5
6
|
|
|
6
7
|
//#region src/utils/contrast-context.tsx
|
|
7
8
|
const CONTRAST_STORAGE_KEY = "contrast";
|
|
@@ -25,9 +26,6 @@ function readExplicitContrast() {
|
|
|
25
26
|
return null;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
function readStoredContrast() {
|
|
29
|
-
return readExplicitContrast() ?? "default";
|
|
30
|
-
}
|
|
31
29
|
function persistContrastPreference(preference) {
|
|
32
30
|
if (typeof window === "undefined") return;
|
|
33
31
|
try {
|
|
@@ -40,9 +38,9 @@ function persistContrastPreference(preference) {
|
|
|
40
38
|
*
|
|
41
39
|
* Pass `initial` when the server already knows the user's preference (e.g. from their account): it
|
|
42
40
|
* wins over this browser's stored value and replaces it, so a new device paints correctly on first
|
|
43
|
-
* load.
|
|
41
|
+
* load. In an SPA, pass the provider's `gateStorageKey` (see `getToneNoFlashScript`).
|
|
44
42
|
*/
|
|
45
|
-
function getContrastNoFlashScript(initial) {
|
|
43
|
+
function getContrastNoFlashScript(initial, { gateStorageKey } = {}) {
|
|
46
44
|
const storageKey = JSON.stringify(CONTRAST_STORAGE_KEY);
|
|
47
45
|
const allowed = JSON.stringify(CONTRAST_PREFERENCES);
|
|
48
46
|
const defaultPreference = JSON.stringify("default");
|
|
@@ -52,7 +50,7 @@ function getContrastNoFlashScript(initial) {
|
|
|
52
50
|
const initial = ${JSON.stringify(initial ?? null)}
|
|
53
51
|
let contrast = ${defaultPreference}
|
|
54
52
|
try {
|
|
55
|
-
if (allowed.includes(initial)) {
|
|
53
|
+
${gateScriptGuard(gateStorageKey)}if (allowed.includes(initial)) {
|
|
56
54
|
contrast = initial
|
|
57
55
|
localStorage.setItem(${storageKey}, JSON.stringify(initial))
|
|
58
56
|
} else {
|
|
@@ -74,69 +72,62 @@ function getContrastNoFlashScript(initial) {
|
|
|
74
72
|
* with `getContrastNoFlashScript()` in the document head so the stored overlay applies before
|
|
75
73
|
* hydration.
|
|
76
74
|
*
|
|
77
|
-
* SSR-safe:
|
|
78
|
-
*
|
|
75
|
+
* SSR-safe: the first render uses `initialContrast ?? 'default'` and storage is read after mount.
|
|
76
|
+
* While disabled, `<html>` is touched only to clear an overlay this feature applied.
|
|
79
77
|
*/
|
|
80
78
|
function ContrastProvider(t0) {
|
|
81
|
-
const $ = c(
|
|
82
|
-
const { children, enabled: t1, initialContrast } = t0;
|
|
79
|
+
const $ = c(12);
|
|
80
|
+
const { children, enabled: t1, initialContrast, gateStorageKey } = t0;
|
|
83
81
|
const enabled = t1 === void 0 ? true : t1;
|
|
84
82
|
let t2;
|
|
85
|
-
if ($[0] !== initialContrast) {
|
|
86
|
-
t2 =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
if ($[0] !== enabled || $[1] !== gateStorageKey || $[2] !== initialContrast) {
|
|
84
|
+
t2 = {
|
|
85
|
+
enabled,
|
|
86
|
+
initial: initialContrast,
|
|
87
|
+
fallback: "default",
|
|
88
|
+
readStored: readExplicitContrast,
|
|
89
|
+
apply: applyContrastAttribute,
|
|
90
|
+
gateStorageKey
|
|
91
|
+
};
|
|
92
|
+
$[0] = enabled;
|
|
93
|
+
$[1] = gateStorageKey;
|
|
94
|
+
$[2] = initialContrast;
|
|
95
|
+
$[3] = t2;
|
|
96
|
+
} else t2 = $[3];
|
|
97
|
+
const [storedContrast, setContrastState] = useRootSetting(t2);
|
|
91
98
|
const contrast = enabled ? storedContrast : "default";
|
|
92
99
|
let t3;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
t3 = () => {
|
|
96
|
-
applyContrastAttribute(contrast);
|
|
97
|
-
};
|
|
98
|
-
t4 = [contrast];
|
|
99
|
-
$[2] = contrast;
|
|
100
|
-
$[3] = t3;
|
|
101
|
-
$[4] = t4;
|
|
102
|
-
} else {
|
|
103
|
-
t3 = $[3];
|
|
104
|
-
t4 = $[4];
|
|
105
|
-
}
|
|
106
|
-
useEffect(t3, t4);
|
|
107
|
-
let t5;
|
|
108
|
-
if ($[5] !== enabled) {
|
|
109
|
-
t5 = (next) => {
|
|
100
|
+
if ($[4] !== setContrastState) {
|
|
101
|
+
t3 = (next) => {
|
|
110
102
|
persistContrastPreference(next);
|
|
111
|
-
if (enabled) applyContrastAttribute(next);
|
|
112
103
|
setContrastState(next);
|
|
113
104
|
};
|
|
114
|
-
$[
|
|
115
|
-
$[
|
|
116
|
-
} else
|
|
117
|
-
const setContrast =
|
|
118
|
-
let
|
|
119
|
-
if ($[
|
|
120
|
-
|
|
105
|
+
$[4] = setContrastState;
|
|
106
|
+
$[5] = t3;
|
|
107
|
+
} else t3 = $[5];
|
|
108
|
+
const setContrast = t3;
|
|
109
|
+
let t4;
|
|
110
|
+
if ($[6] !== contrast || $[7] !== setContrast) {
|
|
111
|
+
t4 = {
|
|
121
112
|
contrast,
|
|
122
113
|
setContrast
|
|
123
114
|
};
|
|
124
|
-
$[
|
|
125
|
-
$[
|
|
126
|
-
$[
|
|
127
|
-
} else
|
|
128
|
-
const value =
|
|
129
|
-
let
|
|
130
|
-
if ($[
|
|
131
|
-
|
|
115
|
+
$[6] = contrast;
|
|
116
|
+
$[7] = setContrast;
|
|
117
|
+
$[8] = t4;
|
|
118
|
+
} else t4 = $[8];
|
|
119
|
+
const value = t4;
|
|
120
|
+
let t5;
|
|
121
|
+
if ($[9] !== children || $[10] !== value) {
|
|
122
|
+
t5 = /* @__PURE__ */ jsx(ContrastContext.Provider, {
|
|
132
123
|
value,
|
|
133
124
|
children
|
|
134
125
|
});
|
|
135
|
-
$[
|
|
136
|
-
$[
|
|
137
|
-
$[
|
|
138
|
-
} else
|
|
139
|
-
return
|
|
126
|
+
$[9] = children;
|
|
127
|
+
$[10] = value;
|
|
128
|
+
$[11] = t5;
|
|
129
|
+
} else t5 = $[11];
|
|
130
|
+
return t5;
|
|
140
131
|
}
|
|
141
132
|
function useContrast() {
|
|
142
133
|
const context = useContext(ContrastContext);
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { gateScriptGuard, useRootSetting } from "./root-setting.js";
|
|
2
|
+
import { useAccountSetting } from "./user-settings.js";
|
|
3
|
+
import { DENSITY_DEFAULT, DENSITY_STEPS, DENSITY_STOPS, applyDensity, snapDensity } from "../lib/utils/density.js";
|
|
4
|
+
import { c } from "react/compiler-runtime";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import React, { createContext, useContext } from "react";
|
|
7
|
+
|
|
8
|
+
//#region src/utils/density-context.tsx
|
|
9
|
+
const DENSITY_STORAGE_KEY = "density";
|
|
10
|
+
/** User-settings key for the density preference. Value shape: `{ density: number }`. */
|
|
11
|
+
const DENSITY_SETTING_KEY = "settings:density";
|
|
12
|
+
const DISABLED_DENSITY = {
|
|
13
|
+
available: false,
|
|
14
|
+
density: 1,
|
|
15
|
+
setDensity: () => void 0
|
|
16
|
+
};
|
|
17
|
+
const DensityContext = createContext(DISABLED_DENSITY);
|
|
18
|
+
/** Reads a stored density, either `{ density }`, a bare number, or a numeric string. */
|
|
19
|
+
function parseDensitySetting(value) {
|
|
20
|
+
const raw = typeof value === "object" && value !== null ? value.density : value;
|
|
21
|
+
const parsed = typeof raw === "string" ? Number.parseFloat(raw) : raw;
|
|
22
|
+
if (typeof parsed !== "number" || !Number.isFinite(parsed)) return null;
|
|
23
|
+
return snapDensity(parsed);
|
|
24
|
+
}
|
|
25
|
+
function applyRootDensity(density) {
|
|
26
|
+
if (typeof document === "undefined") return;
|
|
27
|
+
applyDensity(document.documentElement, density);
|
|
28
|
+
}
|
|
29
|
+
/** The density saved in this browser, or `null` if the user never chose one here. */
|
|
30
|
+
function readExplicitDensity() {
|
|
31
|
+
if (typeof window === "undefined") return null;
|
|
32
|
+
try {
|
|
33
|
+
return parseDensitySetting(window.localStorage.getItem(DENSITY_STORAGE_KEY));
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function persistDensity(density) {
|
|
39
|
+
if (typeof window === "undefined") return;
|
|
40
|
+
try {
|
|
41
|
+
window.localStorage.setItem(DENSITY_STORAGE_KEY, String(density));
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Inline script that applies the density before React hydrates. Render it only while the density
|
|
46
|
+
* feature is on. Snaps to `DENSITY_STEPS` like `snapDensity` (ties toward 1) and then does what
|
|
47
|
+
* `applyDensity` does: a named stop sets `data-density="<name>"`, an in-between step sets
|
|
48
|
+
* `data-density=""` plus an inline `--pui-density`, and the default clears both. Pass `initial`
|
|
49
|
+
* when the server already knows the user's density: it wins and replaces the stored one. In an SPA,
|
|
50
|
+
* pass the provider's `gateStorageKey` (see `getToneNoFlashScript`).
|
|
51
|
+
*/
|
|
52
|
+
function getDensityNoFlashScript(initial, { gateStorageKey } = {}) {
|
|
53
|
+
const storageKey = JSON.stringify(DENSITY_STORAGE_KEY);
|
|
54
|
+
const initialDensity = JSON.stringify(initial ?? null);
|
|
55
|
+
const stops = JSON.stringify(Object.fromEntries(DENSITY_STOPS.map((s) => [s.value, s.name])));
|
|
56
|
+
return `;(() => {
|
|
57
|
+
try {
|
|
58
|
+
${gateScriptGuard(gateStorageKey)}const initial = ${initialDensity}
|
|
59
|
+
if (typeof initial === 'number') localStorage.setItem(${storageKey}, String(initial))
|
|
60
|
+
const stored = typeof initial === 'number' ? initial : parseFloat(localStorage.getItem(${storageKey}) || '1')
|
|
61
|
+
const raw = Math.min(1.5, Math.max(0.75, stored || 1))
|
|
62
|
+
const steps = ${JSON.stringify(DENSITY_STEPS)}
|
|
63
|
+
let d = 1
|
|
64
|
+
for (const step of steps) {
|
|
65
|
+
const gap = Math.abs(step - raw) - Math.abs(d - raw)
|
|
66
|
+
if (gap < -1e-9 || (Math.abs(gap) <= 1e-9 && Math.abs(step - 1) < Math.abs(d - 1))) d = step
|
|
67
|
+
}
|
|
68
|
+
const root = document.documentElement
|
|
69
|
+
const stop = ${stops}[String(d)]
|
|
70
|
+
root.style.removeProperty('--pui-density')
|
|
71
|
+
if (d === 1) {
|
|
72
|
+
root.removeAttribute('data-density')
|
|
73
|
+
} else if (stop) {
|
|
74
|
+
root.setAttribute('data-density', stop)
|
|
75
|
+
} else {
|
|
76
|
+
root.setAttribute('data-density', '')
|
|
77
|
+
root.style.setProperty('--pui-density', String(d))
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
})()`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* App-wide density (Scale) provider. Persists to localStorage (key `density`) and applies the
|
|
84
|
+
* density to `<html>` with `applyDensity`. Pair with `getDensityNoFlashScript()` in the head.
|
|
85
|
+
*
|
|
86
|
+
* SSR-safe: the first render uses `initialDensity ?? 1` and storage is read after mount. While
|
|
87
|
+
* disabled, `<html>` is touched only to clear a density this feature applied.
|
|
88
|
+
*/
|
|
89
|
+
function DensityProvider(t0) {
|
|
90
|
+
const $ = c(13);
|
|
91
|
+
const { children, enabled: t1, initialDensity, gateStorageKey } = t0;
|
|
92
|
+
const enabled = t1 === void 0 ? true : t1;
|
|
93
|
+
let t2;
|
|
94
|
+
if ($[0] !== initialDensity) {
|
|
95
|
+
t2 = initialDensity === void 0 ? void 0 : snapDensity(initialDensity);
|
|
96
|
+
$[0] = initialDensity;
|
|
97
|
+
$[1] = t2;
|
|
98
|
+
} else t2 = $[1];
|
|
99
|
+
let t3;
|
|
100
|
+
if ($[2] !== enabled || $[3] !== gateStorageKey || $[4] !== t2) {
|
|
101
|
+
t3 = {
|
|
102
|
+
enabled,
|
|
103
|
+
initial: t2,
|
|
104
|
+
fallback: 1,
|
|
105
|
+
readStored: readExplicitDensity,
|
|
106
|
+
apply: applyRootDensity,
|
|
107
|
+
gateStorageKey
|
|
108
|
+
};
|
|
109
|
+
$[2] = enabled;
|
|
110
|
+
$[3] = gateStorageKey;
|
|
111
|
+
$[4] = t2;
|
|
112
|
+
$[5] = t3;
|
|
113
|
+
} else t3 = $[5];
|
|
114
|
+
const [storedDensity, setDensityState] = useRootSetting(t3);
|
|
115
|
+
const density = enabled ? storedDensity : 1;
|
|
116
|
+
let t4;
|
|
117
|
+
if ($[6] !== density || $[7] !== enabled || $[8] !== setDensityState) {
|
|
118
|
+
t4 = enabled ? {
|
|
119
|
+
available: true,
|
|
120
|
+
density,
|
|
121
|
+
setDensity: (next) => {
|
|
122
|
+
const snapped = snapDensity(next);
|
|
123
|
+
persistDensity(snapped);
|
|
124
|
+
setDensityState(snapped);
|
|
125
|
+
}
|
|
126
|
+
} : DISABLED_DENSITY;
|
|
127
|
+
$[6] = density;
|
|
128
|
+
$[7] = enabled;
|
|
129
|
+
$[8] = setDensityState;
|
|
130
|
+
$[9] = t4;
|
|
131
|
+
} else t4 = $[9];
|
|
132
|
+
const value = t4;
|
|
133
|
+
let t5;
|
|
134
|
+
if ($[10] !== children || $[11] !== value) {
|
|
135
|
+
t5 = /* @__PURE__ */ jsx(DensityContext.Provider, {
|
|
136
|
+
value,
|
|
137
|
+
children
|
|
138
|
+
});
|
|
139
|
+
$[10] = children;
|
|
140
|
+
$[11] = value;
|
|
141
|
+
$[12] = t5;
|
|
142
|
+
} else t5 = $[12];
|
|
143
|
+
return t5;
|
|
144
|
+
}
|
|
145
|
+
/** Current density and its setter. Safe anywhere; outside an enabled provider it is unavailable. */
|
|
146
|
+
function useDensity() {
|
|
147
|
+
return useContext(DensityContext);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* `useDensity` plus the user's account copy, so the density follows them across apps, browsers, and
|
|
151
|
+
* devices. Use it where the density control lives, inside `DensityProvider`. Pass the same feature
|
|
152
|
+
* gate as the provider's `enabled`.
|
|
153
|
+
*/
|
|
154
|
+
function useAccountDensity(t0) {
|
|
155
|
+
const $ = c(12);
|
|
156
|
+
const { client, enabled: t1 } = t0;
|
|
157
|
+
const enabled = t1 === void 0 ? true : t1;
|
|
158
|
+
const { available, density, setDensity } = useDensity();
|
|
159
|
+
const t2 = enabled && available;
|
|
160
|
+
let t3;
|
|
161
|
+
if ($[0] !== client || $[1] !== density || $[2] !== setDensity || $[3] !== t2) {
|
|
162
|
+
t3 = {
|
|
163
|
+
client,
|
|
164
|
+
key: DENSITY_SETTING_KEY,
|
|
165
|
+
enabled: t2,
|
|
166
|
+
value: density,
|
|
167
|
+
readExplicitValue: readExplicitDensity,
|
|
168
|
+
parse: parseDensitySetting,
|
|
169
|
+
serialize: _temp,
|
|
170
|
+
onRemoteValue: setDensity
|
|
171
|
+
};
|
|
172
|
+
$[0] = client;
|
|
173
|
+
$[1] = density;
|
|
174
|
+
$[2] = setDensity;
|
|
175
|
+
$[3] = t2;
|
|
176
|
+
$[4] = t3;
|
|
177
|
+
} else t3 = $[4];
|
|
178
|
+
const save = useAccountSetting(t3);
|
|
179
|
+
let t4;
|
|
180
|
+
if ($[5] !== save || $[6] !== setDensity) {
|
|
181
|
+
t4 = (next_0) => {
|
|
182
|
+
const snapped = snapDensity(next_0);
|
|
183
|
+
setDensity(snapped);
|
|
184
|
+
save(snapped);
|
|
185
|
+
};
|
|
186
|
+
$[5] = save;
|
|
187
|
+
$[6] = setDensity;
|
|
188
|
+
$[7] = t4;
|
|
189
|
+
} else t4 = $[7];
|
|
190
|
+
let t5;
|
|
191
|
+
if ($[8] !== available || $[9] !== density || $[10] !== t4) {
|
|
192
|
+
t5 = {
|
|
193
|
+
available,
|
|
194
|
+
density,
|
|
195
|
+
setDensity: t4
|
|
196
|
+
};
|
|
197
|
+
$[8] = available;
|
|
198
|
+
$[9] = density;
|
|
199
|
+
$[10] = t4;
|
|
200
|
+
$[11] = t5;
|
|
201
|
+
} else t5 = $[11];
|
|
202
|
+
return t5;
|
|
203
|
+
}
|
|
204
|
+
function _temp(next) {
|
|
205
|
+
return { density: next };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
export { DENSITY_SETTING_KEY, DENSITY_STORAGE_KEY, DensityContext, DensityProvider, getDensityNoFlashScript, parseDensitySetting, readExplicitDensity, useAccountDensity, useDensity };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/root-setting.ts
|
|
4
|
+
/** Saves a feature's on/off flag for a gated no-flash script. Storage errors are ignored. */
|
|
5
|
+
function writeGateFlag(key, enabled) {
|
|
6
|
+
try {
|
|
7
|
+
window.localStorage.setItem(key, String(enabled));
|
|
8
|
+
} catch {}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* State for one appearance setting that lives on `<html>` (tone, density, contrast). Shared by the
|
|
12
|
+
* providers so they hydrate and clean up the same way:
|
|
13
|
+
*
|
|
14
|
+
* - The first render uses `initial ?? fallback`, never `localStorage`, so it matches the server
|
|
15
|
+
* render. The stored value is read after mount; the no-flash script has already painted it.
|
|
16
|
+
* - While `enabled`, the value is applied to `<html>`.
|
|
17
|
+
* - While disabled, `<html>` is cleared only if this feature may have painted it (it was enabled
|
|
18
|
+
* earlier in this mount, or this browser has a stored value the no-flash script applied). A
|
|
19
|
+
* browser that never used the feature is never touched.
|
|
20
|
+
* - `gateStorageKey` (SPAs only) saves `enabled` so the gated no-flash script can skip a feature that
|
|
21
|
+
* is off.
|
|
22
|
+
*/
|
|
23
|
+
function useRootSetting({ enabled, initial, fallback, readStored, apply, gateStorageKey }) {
|
|
24
|
+
const [value, setValue] = useState(initial ?? fallback);
|
|
25
|
+
const [ready, setReady] = useState(initial !== void 0);
|
|
26
|
+
const wasEnabledRef = useRef(false);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (ready) return;
|
|
29
|
+
const stored = readStored();
|
|
30
|
+
if (stored !== null) setValue(stored);
|
|
31
|
+
setReady(true);
|
|
32
|
+
}, [ready, readStored]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!ready) return;
|
|
35
|
+
if (gateStorageKey) writeGateFlag(gateStorageKey, enabled);
|
|
36
|
+
if (enabled) {
|
|
37
|
+
wasEnabledRef.current = true;
|
|
38
|
+
apply(value);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (wasEnabledRef.current || readStored() !== null) apply(fallback);
|
|
42
|
+
wasEnabledRef.current = false;
|
|
43
|
+
}, [
|
|
44
|
+
ready,
|
|
45
|
+
enabled,
|
|
46
|
+
value,
|
|
47
|
+
fallback,
|
|
48
|
+
readStored,
|
|
49
|
+
apply,
|
|
50
|
+
gateStorageKey
|
|
51
|
+
]);
|
|
52
|
+
return [value, setValue];
|
|
53
|
+
}
|
|
54
|
+
/** Script prefix for a gated no-flash script: stop unless the saved flag is `true`. */
|
|
55
|
+
function gateScriptGuard(gateStorageKey) {
|
|
56
|
+
if (!gateStorageKey) return "";
|
|
57
|
+
return `if (localStorage.getItem(${JSON.stringify(gateStorageKey)}) !== 'true') return\n `;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { gateScriptGuard, useRootSetting };
|