@etamong-playground/ui 0.34.2

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.
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ // src/keywords.ts
4
+ function crossLocaleKeywords(dicts, getter) {
5
+ return dicts.map(getter).filter(Boolean).join(" ");
6
+ }
7
+ function isInputTarget(e) {
8
+ const t = e.target;
9
+ if (!t) return false;
10
+ return t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable;
11
+ }
12
+ var CODE_TO_KEY = {
13
+ Slash: "/",
14
+ KeyG: "g",
15
+ KeyH: "h",
16
+ KeyK: "k",
17
+ KeyS: "s",
18
+ KeyN: "n",
19
+ KeyT: "t",
20
+ KeyM: "m",
21
+ KeyP: "p"
22
+ };
23
+ function shortcutKey(e) {
24
+ const isAscii = e.key === "/" || e.key === "?" || /^[a-z]$/.test(e.key);
25
+ return isAscii ? e.key : CODE_TO_KEY[e.code] ?? e.key;
26
+ }
27
+ var COMMAND_PALETTE_OPEN_EVENT = "command-palette:open";
28
+ function openCommandPalette() {
29
+ document.dispatchEvent(new CustomEvent(COMMAND_PALETTE_OPEN_EVENT));
30
+ }
31
+
32
+ // src/theme.ts
33
+ function storageKey(appKey) {
34
+ return `${appKey}-theme`;
35
+ }
36
+ function noFlashThemeScript(appKey) {
37
+ return `(function(){try{var k=${JSON.stringify(storageKey(appKey))};var s=localStorage.getItem(k);var d=s;if(d!=="light"&&d!=="dark"){d=(window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches)?"light":"dark";}document.documentElement.setAttribute("data-theme",d);}catch(e){}})();`;
38
+ }
39
+ function getTheme(appKey) {
40
+ try {
41
+ const saved = localStorage.getItem(storageKey(appKey));
42
+ if (saved === "light" || saved === "dark") return saved;
43
+ } catch {
44
+ }
45
+ if (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) {
46
+ return "light";
47
+ }
48
+ return "dark";
49
+ }
50
+ function setTheme(appKey, theme) {
51
+ try {
52
+ localStorage.setItem(storageKey(appKey), theme);
53
+ } catch {
54
+ }
55
+ document.documentElement.setAttribute("data-theme", theme);
56
+ }
57
+
58
+ // src/i18nCore.ts
59
+ var SUPPORTED_LOCALES = ["ko", "en"];
60
+ function storageKey2(appKey) {
61
+ return `${appKey}-locale`;
62
+ }
63
+ function detectSystemLocale() {
64
+ if (typeof navigator === "undefined") return "en";
65
+ const candidates = [];
66
+ if (Array.isArray(navigator.languages)) {
67
+ candidates.push(...navigator.languages);
68
+ } else if (typeof navigator.language === "string") {
69
+ candidates.push(navigator.language);
70
+ }
71
+ for (const raw of candidates) {
72
+ const tag = raw.toLowerCase();
73
+ if (tag === "ko" || tag.startsWith("ko-")) return "ko";
74
+ if (tag === "en" || tag.startsWith("en-")) return "en";
75
+ }
76
+ return "en";
77
+ }
78
+ function getLocale(appKey) {
79
+ try {
80
+ const saved = localStorage.getItem(storageKey2(appKey));
81
+ if (saved === "ko" || saved === "en") return saved;
82
+ } catch {
83
+ }
84
+ return detectSystemLocale();
85
+ }
86
+ function setLocale(appKey, locale) {
87
+ try {
88
+ localStorage.setItem(storageKey2(appKey), locale);
89
+ } catch {
90
+ }
91
+ if (typeof document !== "undefined") {
92
+ document.documentElement.setAttribute("lang", locale);
93
+ }
94
+ }
95
+ function noFlashLocaleScript(appKey) {
96
+ return `(function(){try{var k=${JSON.stringify(storageKey2(appKey))};var s=localStorage.getItem(k);var l=s;if(l!=="ko"&&l!=="en"){l="en";var langs=(navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language||""]);for(var i=0;i<langs.length;i++){var t=(langs[i]||"").toLowerCase();if(t==="ko"||t.indexOf("ko-")===0){l="ko";break;}if(t==="en"||t.indexOf("en-")===0){l="en";break;}}}document.documentElement.setAttribute("lang",l);}catch(e){}})();`;
97
+ }
98
+ function interpolate(template, vars) {
99
+ if (!vars) return template;
100
+ return template.replace(/\{(\w+)\}/g, (m, k) => {
101
+ const v = vars[k];
102
+ return v === void 0 || v === null ? m : String(v);
103
+ });
104
+ }
105
+
106
+ // src/viewportCore.ts
107
+ var TABLET_MIN = 720;
108
+ var DESKTOP_MIN = 1024;
109
+ function tierForWidth(w) {
110
+ if (w >= DESKTOP_MIN) return "desktop";
111
+ if (w >= TABLET_MIN) return "tablet";
112
+ return "mobile";
113
+ }
114
+ function getViewport() {
115
+ if (typeof window === "undefined") return "desktop";
116
+ return tierForWidth(window.innerWidth);
117
+ }
118
+ var noFlashViewportScript = `(function(){try{var w=window.innerWidth||document.documentElement.clientWidth;var v="mobile";if(w>=${DESKTOP_MIN})v="desktop";else if(w>=${TABLET_MIN})v="tablet";document.documentElement.setAttribute("data-vp",v);}catch(e){}})();`;
119
+
120
+ exports.CODE_TO_KEY = CODE_TO_KEY;
121
+ exports.COMMAND_PALETTE_OPEN_EVENT = COMMAND_PALETTE_OPEN_EVENT;
122
+ exports.DESKTOP_MIN = DESKTOP_MIN;
123
+ exports.SUPPORTED_LOCALES = SUPPORTED_LOCALES;
124
+ exports.TABLET_MIN = TABLET_MIN;
125
+ exports.crossLocaleKeywords = crossLocaleKeywords;
126
+ exports.getLocale = getLocale;
127
+ exports.getTheme = getTheme;
128
+ exports.getViewport = getViewport;
129
+ exports.interpolate = interpolate;
130
+ exports.isInputTarget = isInputTarget;
131
+ exports.noFlashLocaleScript = noFlashLocaleScript;
132
+ exports.noFlashThemeScript = noFlashThemeScript;
133
+ exports.noFlashViewportScript = noFlashViewportScript;
134
+ exports.openCommandPalette = openCommandPalette;
135
+ exports.setLocale = setLocale;
136
+ exports.setTheme = setTheme;
137
+ exports.shortcutKey = shortcutKey;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Concatenate one label across every locale dictionary so cmdk search matches
3
+ * regardless of the active language — a Korean user typing an English term (or
4
+ * vice-versa) still finds the item. This is mandatory in a ko-first ecosystem.
5
+ *
6
+ * @example
7
+ * import ko from "./locales/ko";
8
+ * import en from "./locales/en";
9
+ * const dicts = [ko, en];
10
+ * crossLocaleKeywords(dicts, (d) => d.nav.schedules) // "일정 Schedules"
11
+ */
12
+ declare function crossLocaleKeywords<D>(dicts: readonly D[], getter: (dict: D) => string): string;
13
+ /** True when the keyboard event originates from a text-entry control. */
14
+ declare function isInputTarget(e: KeyboardEvent): boolean;
15
+ /**
16
+ * Map physical key codes to logical keys so shortcuts survive a Korean IME:
17
+ * when an IME is active `e.key` is a Hangul syllable, not the Latin letter, so
18
+ * single-letter shortcuts must fall back to `e.code`.
19
+ */
20
+ declare const CODE_TO_KEY: Readonly<Record<string, string>>;
21
+ /**
22
+ * Resolve the logical shortcut key for an event. Prefers `e.key` when it is
23
+ * already an ASCII shortcut (`/`, `?`, or a–z); otherwise falls back to the
24
+ * physical-code map (covers IME output).
25
+ */
26
+ declare function shortcutKey(e: KeyboardEvent): string;
27
+ /** The custom DOM event any UI affordance can dispatch to open the palette. */
28
+ declare const COMMAND_PALETTE_OPEN_EVENT = "command-palette:open";
29
+ /** Dispatch `command-palette:open` so a button/menu can open the palette. */
30
+ declare function openCommandPalette(): void;
31
+
32
+ /**
33
+ * Theme helpers for the `[data-theme]` mechanism that styles.css implements.
34
+ *
35
+ * `data-theme` must be set on <html> BEFORE first paint, or the page flashes
36
+ * the default theme before flipping to the user's choice. Inject
37
+ * `noFlashThemeScript` synchronously in <head> — for Next, in a <script
38
+ * dangerouslySetInnerHTML>; for a Vite app, inline in index.html.
39
+ *
40
+ * Resolution order (matches the fleet rule in
41
+ * planning/wiki/concepts/theme-system-dark-fallback.md):
42
+ *
43
+ * 1. saved user choice in localStorage
44
+ * 2. OS preference via `prefers-color-scheme`
45
+ * 3. dark — the fleet-wide fallback when we can't tell
46
+ *
47
+ * Prior to v0.28 the fallback was "light"; the fleet now treats dark as
48
+ * the audience-of-record default (most surfaces are dark; designs assume
49
+ * dark first).
50
+ */
51
+ type Theme = "light" | "dark";
52
+ /**
53
+ * A self-contained <head> snippet (no deps) that sets `data-theme` from the
54
+ * saved choice, falling back to the OS preference, then dark. Pass the same
55
+ * `appKey` you pass to `getTheme`/`setTheme`.
56
+ */
57
+ declare function noFlashThemeScript(appKey: string): string;
58
+ /** Read the active theme (saved choice → OS preference → dark). */
59
+ declare function getTheme(appKey: string): Theme;
60
+ /** Persist and apply a theme to <html>. */
61
+ declare function setTheme(appKey: string, theme: Theme): void;
62
+
63
+ /**
64
+ * React-free i18n primitives — safe to import from `helpers.ts`, server
65
+ * runtimes, or the index.html-adjacent script that emits the no-flash
66
+ * snippet. The React provider/hooks live in `i18n.tsx` and re-export
67
+ * everything from here.
68
+ */
69
+ type Locale = "ko" | "en";
70
+ declare const SUPPORTED_LOCALES: Locale[];
71
+ type Messages = Record<string, string>;
72
+ type MessageBundle = Record<Locale, Messages>;
73
+ /** Read the active locale (saved choice → system → "en"). */
74
+ declare function getLocale(appKey: string): Locale;
75
+ /** Persist and apply a locale to <html lang>. */
76
+ declare function setLocale(appKey: string, locale: Locale): void;
77
+ /**
78
+ * <head> snippet (no deps) that sets <html lang> from the saved choice,
79
+ * falling back to system languages, then "en".
80
+ */
81
+ declare function noFlashLocaleScript(appKey: string): string;
82
+ /** Render `{name}` placeholders. Unknown placeholders pass through. */
83
+ declare function interpolate(template: string, vars?: Record<string, string | number>): string;
84
+
85
+ /**
86
+ * React-free viewport primitives — safe to import from `helpers.ts`.
87
+ * React provider/hook lives in `viewport.tsx`.
88
+ */
89
+ type ViewportTier = "mobile" | "tablet" | "desktop";
90
+ declare const TABLET_MIN = 720;
91
+ declare const DESKTOP_MIN = 1024;
92
+ /** SSR-safe — returns "desktop" on the server. */
93
+ declare function getViewport(): ViewportTier;
94
+ declare const noFlashViewportScript: string;
95
+
96
+ export { CODE_TO_KEY, COMMAND_PALETTE_OPEN_EVENT, DESKTOP_MIN, type Locale, type MessageBundle, type Messages, SUPPORTED_LOCALES, TABLET_MIN, type Theme, type ViewportTier, crossLocaleKeywords, getLocale, getTheme, getViewport, interpolate, isInputTarget, noFlashLocaleScript, noFlashThemeScript, noFlashViewportScript, openCommandPalette, setLocale, setTheme, shortcutKey };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Concatenate one label across every locale dictionary so cmdk search matches
3
+ * regardless of the active language — a Korean user typing an English term (or
4
+ * vice-versa) still finds the item. This is mandatory in a ko-first ecosystem.
5
+ *
6
+ * @example
7
+ * import ko from "./locales/ko";
8
+ * import en from "./locales/en";
9
+ * const dicts = [ko, en];
10
+ * crossLocaleKeywords(dicts, (d) => d.nav.schedules) // "일정 Schedules"
11
+ */
12
+ declare function crossLocaleKeywords<D>(dicts: readonly D[], getter: (dict: D) => string): string;
13
+ /** True when the keyboard event originates from a text-entry control. */
14
+ declare function isInputTarget(e: KeyboardEvent): boolean;
15
+ /**
16
+ * Map physical key codes to logical keys so shortcuts survive a Korean IME:
17
+ * when an IME is active `e.key` is a Hangul syllable, not the Latin letter, so
18
+ * single-letter shortcuts must fall back to `e.code`.
19
+ */
20
+ declare const CODE_TO_KEY: Readonly<Record<string, string>>;
21
+ /**
22
+ * Resolve the logical shortcut key for an event. Prefers `e.key` when it is
23
+ * already an ASCII shortcut (`/`, `?`, or a–z); otherwise falls back to the
24
+ * physical-code map (covers IME output).
25
+ */
26
+ declare function shortcutKey(e: KeyboardEvent): string;
27
+ /** The custom DOM event any UI affordance can dispatch to open the palette. */
28
+ declare const COMMAND_PALETTE_OPEN_EVENT = "command-palette:open";
29
+ /** Dispatch `command-palette:open` so a button/menu can open the palette. */
30
+ declare function openCommandPalette(): void;
31
+
32
+ /**
33
+ * Theme helpers for the `[data-theme]` mechanism that styles.css implements.
34
+ *
35
+ * `data-theme` must be set on <html> BEFORE first paint, or the page flashes
36
+ * the default theme before flipping to the user's choice. Inject
37
+ * `noFlashThemeScript` synchronously in <head> — for Next, in a <script
38
+ * dangerouslySetInnerHTML>; for a Vite app, inline in index.html.
39
+ *
40
+ * Resolution order (matches the fleet rule in
41
+ * planning/wiki/concepts/theme-system-dark-fallback.md):
42
+ *
43
+ * 1. saved user choice in localStorage
44
+ * 2. OS preference via `prefers-color-scheme`
45
+ * 3. dark — the fleet-wide fallback when we can't tell
46
+ *
47
+ * Prior to v0.28 the fallback was "light"; the fleet now treats dark as
48
+ * the audience-of-record default (most surfaces are dark; designs assume
49
+ * dark first).
50
+ */
51
+ type Theme = "light" | "dark";
52
+ /**
53
+ * A self-contained <head> snippet (no deps) that sets `data-theme` from the
54
+ * saved choice, falling back to the OS preference, then dark. Pass the same
55
+ * `appKey` you pass to `getTheme`/`setTheme`.
56
+ */
57
+ declare function noFlashThemeScript(appKey: string): string;
58
+ /** Read the active theme (saved choice → OS preference → dark). */
59
+ declare function getTheme(appKey: string): Theme;
60
+ /** Persist and apply a theme to <html>. */
61
+ declare function setTheme(appKey: string, theme: Theme): void;
62
+
63
+ /**
64
+ * React-free i18n primitives — safe to import from `helpers.ts`, server
65
+ * runtimes, or the index.html-adjacent script that emits the no-flash
66
+ * snippet. The React provider/hooks live in `i18n.tsx` and re-export
67
+ * everything from here.
68
+ */
69
+ type Locale = "ko" | "en";
70
+ declare const SUPPORTED_LOCALES: Locale[];
71
+ type Messages = Record<string, string>;
72
+ type MessageBundle = Record<Locale, Messages>;
73
+ /** Read the active locale (saved choice → system → "en"). */
74
+ declare function getLocale(appKey: string): Locale;
75
+ /** Persist and apply a locale to <html lang>. */
76
+ declare function setLocale(appKey: string, locale: Locale): void;
77
+ /**
78
+ * <head> snippet (no deps) that sets <html lang> from the saved choice,
79
+ * falling back to system languages, then "en".
80
+ */
81
+ declare function noFlashLocaleScript(appKey: string): string;
82
+ /** Render `{name}` placeholders. Unknown placeholders pass through. */
83
+ declare function interpolate(template: string, vars?: Record<string, string | number>): string;
84
+
85
+ /**
86
+ * React-free viewport primitives — safe to import from `helpers.ts`.
87
+ * React provider/hook lives in `viewport.tsx`.
88
+ */
89
+ type ViewportTier = "mobile" | "tablet" | "desktop";
90
+ declare const TABLET_MIN = 720;
91
+ declare const DESKTOP_MIN = 1024;
92
+ /** SSR-safe — returns "desktop" on the server. */
93
+ declare function getViewport(): ViewportTier;
94
+ declare const noFlashViewportScript: string;
95
+
96
+ export { CODE_TO_KEY, COMMAND_PALETTE_OPEN_EVENT, DESKTOP_MIN, type Locale, type MessageBundle, type Messages, SUPPORTED_LOCALES, TABLET_MIN, type Theme, type ViewportTier, crossLocaleKeywords, getLocale, getTheme, getViewport, interpolate, isInputTarget, noFlashLocaleScript, noFlashThemeScript, noFlashViewportScript, openCommandPalette, setLocale, setTheme, shortcutKey };
@@ -0,0 +1,118 @@
1
+ // src/keywords.ts
2
+ function crossLocaleKeywords(dicts, getter) {
3
+ return dicts.map(getter).filter(Boolean).join(" ");
4
+ }
5
+ function isInputTarget(e) {
6
+ const t = e.target;
7
+ if (!t) return false;
8
+ return t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable;
9
+ }
10
+ var CODE_TO_KEY = {
11
+ Slash: "/",
12
+ KeyG: "g",
13
+ KeyH: "h",
14
+ KeyK: "k",
15
+ KeyS: "s",
16
+ KeyN: "n",
17
+ KeyT: "t",
18
+ KeyM: "m",
19
+ KeyP: "p"
20
+ };
21
+ function shortcutKey(e) {
22
+ const isAscii = e.key === "/" || e.key === "?" || /^[a-z]$/.test(e.key);
23
+ return isAscii ? e.key : CODE_TO_KEY[e.code] ?? e.key;
24
+ }
25
+ var COMMAND_PALETTE_OPEN_EVENT = "command-palette:open";
26
+ function openCommandPalette() {
27
+ document.dispatchEvent(new CustomEvent(COMMAND_PALETTE_OPEN_EVENT));
28
+ }
29
+
30
+ // src/theme.ts
31
+ function storageKey(appKey) {
32
+ return `${appKey}-theme`;
33
+ }
34
+ function noFlashThemeScript(appKey) {
35
+ return `(function(){try{var k=${JSON.stringify(storageKey(appKey))};var s=localStorage.getItem(k);var d=s;if(d!=="light"&&d!=="dark"){d=(window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches)?"light":"dark";}document.documentElement.setAttribute("data-theme",d);}catch(e){}})();`;
36
+ }
37
+ function getTheme(appKey) {
38
+ try {
39
+ const saved = localStorage.getItem(storageKey(appKey));
40
+ if (saved === "light" || saved === "dark") return saved;
41
+ } catch {
42
+ }
43
+ if (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) {
44
+ return "light";
45
+ }
46
+ return "dark";
47
+ }
48
+ function setTheme(appKey, theme) {
49
+ try {
50
+ localStorage.setItem(storageKey(appKey), theme);
51
+ } catch {
52
+ }
53
+ document.documentElement.setAttribute("data-theme", theme);
54
+ }
55
+
56
+ // src/i18nCore.ts
57
+ var SUPPORTED_LOCALES = ["ko", "en"];
58
+ function storageKey2(appKey) {
59
+ return `${appKey}-locale`;
60
+ }
61
+ function detectSystemLocale() {
62
+ if (typeof navigator === "undefined") return "en";
63
+ const candidates = [];
64
+ if (Array.isArray(navigator.languages)) {
65
+ candidates.push(...navigator.languages);
66
+ } else if (typeof navigator.language === "string") {
67
+ candidates.push(navigator.language);
68
+ }
69
+ for (const raw of candidates) {
70
+ const tag = raw.toLowerCase();
71
+ if (tag === "ko" || tag.startsWith("ko-")) return "ko";
72
+ if (tag === "en" || tag.startsWith("en-")) return "en";
73
+ }
74
+ return "en";
75
+ }
76
+ function getLocale(appKey) {
77
+ try {
78
+ const saved = localStorage.getItem(storageKey2(appKey));
79
+ if (saved === "ko" || saved === "en") return saved;
80
+ } catch {
81
+ }
82
+ return detectSystemLocale();
83
+ }
84
+ function setLocale(appKey, locale) {
85
+ try {
86
+ localStorage.setItem(storageKey2(appKey), locale);
87
+ } catch {
88
+ }
89
+ if (typeof document !== "undefined") {
90
+ document.documentElement.setAttribute("lang", locale);
91
+ }
92
+ }
93
+ function noFlashLocaleScript(appKey) {
94
+ return `(function(){try{var k=${JSON.stringify(storageKey2(appKey))};var s=localStorage.getItem(k);var l=s;if(l!=="ko"&&l!=="en"){l="en";var langs=(navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language||""]);for(var i=0;i<langs.length;i++){var t=(langs[i]||"").toLowerCase();if(t==="ko"||t.indexOf("ko-")===0){l="ko";break;}if(t==="en"||t.indexOf("en-")===0){l="en";break;}}}document.documentElement.setAttribute("lang",l);}catch(e){}})();`;
95
+ }
96
+ function interpolate(template, vars) {
97
+ if (!vars) return template;
98
+ return template.replace(/\{(\w+)\}/g, (m, k) => {
99
+ const v = vars[k];
100
+ return v === void 0 || v === null ? m : String(v);
101
+ });
102
+ }
103
+
104
+ // src/viewportCore.ts
105
+ var TABLET_MIN = 720;
106
+ var DESKTOP_MIN = 1024;
107
+ function tierForWidth(w) {
108
+ if (w >= DESKTOP_MIN) return "desktop";
109
+ if (w >= TABLET_MIN) return "tablet";
110
+ return "mobile";
111
+ }
112
+ function getViewport() {
113
+ if (typeof window === "undefined") return "desktop";
114
+ return tierForWidth(window.innerWidth);
115
+ }
116
+ var noFlashViewportScript = `(function(){try{var w=window.innerWidth||document.documentElement.clientWidth;var v="mobile";if(w>=${DESKTOP_MIN})v="desktop";else if(w>=${TABLET_MIN})v="tablet";document.documentElement.setAttribute("data-vp",v);}catch(e){}})();`;
117
+
118
+ export { CODE_TO_KEY, COMMAND_PALETTE_OPEN_EVENT, DESKTOP_MIN, SUPPORTED_LOCALES, TABLET_MIN, crossLocaleKeywords, getLocale, getTheme, getViewport, interpolate, isInputTarget, noFlashLocaleScript, noFlashThemeScript, noFlashViewportScript, openCommandPalette, setLocale, setTheme, shortcutKey };