diff --git a/src/common/assets/global.css b/src/common/assets/global.css new file mode 100644 index 0000000..3d52b3d --- /dev/null +++ b/src/common/assets/global.css @@ -0,0 +1,28 @@ +:root { + /* Monokai-inspired palette */ + --bg: #272822; + --bg-panel: #1e1f1a; + --bg-hover: #3e3d32; + --bg-active: #49483e; + --border: #3e3d32; + --accent: #f92672; + --accent-alt: #a6e22e; + --text: #f8f8f2; + --text-muted: #75715e; + --text-dim: #cfcfc2; + --yellow: #e6db74; + --orange: #fd971f; + --blue: #66d9ef; + --purple: #ae81ff; + + --radius: 4px; + --transition: 0.15s ease; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + scrollbar-width: thin; + scrollbar-color: var(--bg-active) transparent; +} diff --git a/src/common/components/ContentEditable.tsx b/src/common/components/ContentEditable.tsx index b382563..3c99b93 100644 --- a/src/common/components/ContentEditable.tsx +++ b/src/common/components/ContentEditable.tsx @@ -80,7 +80,7 @@ export const ContentEditable = ({ value, placeholder, autoLines, onInput, class: range.deleteContents(); const endsWithNewline = ref.current.textContent?.endsWith('\n'); - const caretAtEnd = getCaretOffset(ref.current) === ref.current.textContent.length; + const caretAtEnd = getCaretOffset(ref.current) === (ref.current.textContent?.length ?? 0); const newline = document.createTextNode('\n'.repeat((endsWithNewline || !caretAtEnd) ? 1 : 2)); range.insertNode(newline); diff --git a/src/common/display/canvas.ts b/src/common/display/canvas.ts index 90963f3..44516da 100644 --- a/src/common/display/canvas.ts +++ b/src/common/display/canvas.ts @@ -3,7 +3,8 @@ export function loadImageData(dataView: DataView, pointer: number) { const height = dataView.getUint16(pointer + 2, true); const dataPtr = dataView.getUint32(pointer + 4, true); - const imageBuffer = new Uint8Array(dataView.buffer, dataPtr, width * height * 4); + const imageBuffer = new Uint8ClampedArray(dataView.buffer, dataPtr, width * height * 4); + // @ts-ignore β€” Bun's Uint8ClampedArray typedef incorrectly narrows the buffer type const imageData = new ImageData(imageBuffer, width, height); return imageData; diff --git a/src/common/utils.ts b/src/common/utils.ts index f3cd323..97005a8 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -108,5 +108,36 @@ export const throttle = function (func: F, m return wrapper; } -export const callUpdater = (f: StateUpdater, prev: T) => +export const callUpdater = (f: StateUpdater, prev: T) => typeof f === 'function' ? (f as Function)(prev) : f; + +export const formatNumber = (n: number): string => { + if (n >= 1_000_000_000_000) return `${(n / 1_000_000_000_000).toFixed(2)}T`; + if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(2)}B`; + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`; + if (n >= 1_000) return `${(n / 1_000).toFixed(2)}k`; + return String(n); +}; + +export const formatTime = (seconds: number): string => { + const y = Math.floor(seconds / 31536000); + const mo = Math.floor((seconds % 31536000) / 2592000); + const w = Math.floor((seconds % 2592000) / 604800); + const d = Math.floor((seconds % 604800) / 86400); + const h = Math.floor((seconds % 86400) / 3600); + const mi = Math.floor((seconds % 3600) / 60); + const s = Math.floor(seconds % 60); + + const parts: string[] = []; + if (y > 0) parts.push(`${y}y`); + if (mo > 0) parts.push(`${mo}m`); + if (w > 0) parts.push(`${w}w`); + if (d > 0) parts.push(`${d}d`); + + const hasBigParts = parts.length > 0; + const hasTime = h > 0 || mi > 0 || s > 0; + if (hasBigParts || hasTime) { + parts.push(`${h}:${String(mi).padStart(2, '0')}:${String(s).padStart(2, '0')}`); + } + return parts.join(' ') || '0:00:00'; +}; diff --git a/src/games/ai-story/assets/bg.jpg b/src/games/ai-story/assets/bg.jpg deleted file mode 100644 index cec9429..0000000 Binary files a/src/games/ai-story/assets/bg.jpg and /dev/null differ diff --git a/src/games/ai-story/assets/config.yml b/src/games/ai-story/assets/config.yml deleted file mode 100644 index 101d13b..0000000 --- a/src/games/ai-story/assets/config.yml +++ /dev/null @@ -1 +0,0 @@ -isApp: true \ No newline at end of file diff --git a/src/games/ai-story/assets/emoji.css b/src/games/ai-story/assets/emoji.css deleted file mode 100644 index 2027e55..0000000 --- a/src/games/ai-story/assets/emoji.css +++ /dev/null @@ -1,219 +0,0 @@ -/* [0] */ -@font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.0.woff2) format('woff2'); - unicode-range: U+1f1e6-1f1ff; - } - /* [1] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.1.woff2) format('woff2'); - unicode-range: U+200d, U+2620, U+26a7, U+fe0f, U+1f308, U+1f38c, U+1f3c1, U+1f3f3-1f3f4, U+1f6a9, U+e0062-e0063, U+e0065, U+e0067, U+e006c, U+e006e, U+e0073-e0074, U+e0077, U+e007f; - } - /* [2] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.2.woff2) format('woff2'); - unicode-range: U+23, U+2a, U+30-39, U+a9, U+ae, U+200d, U+203c, U+2049, U+20e3, U+2122, U+2139, U+2194-2199, U+21a9-21aa, U+23cf, U+23e9-23ef, U+23f8-23fa, U+24c2, U+25aa-25ab, U+25b6, U+25c0, U+25fb-25fe, U+2611, U+2622-2623, U+2626, U+262a, U+262e-262f, U+2638, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267b, U+267e-267f, U+2695, U+269b-269c, U+26a0, U+26a7, U+26aa-26ab, U+26ce, U+26d4, U+2705, U+2714, U+2716, U+271d, U+2721, U+2733-2734, U+2747, U+274c, U+274e, U+2753-2755, U+2757, U+2764, U+2795-2797, U+27a1, U+27b0, U+27bf, U+2934-2935, U+2b05-2b07, U+2b1b-2b1c, U+2b55, U+3030, U+303d, U+3297, U+3299, U+fe0f, U+1f170-1f171, U+1f17e-1f17f, U+1f18e, U+1f191-1f19a, U+1f201-1f202, U+1f21a, U+1f22f, U+1f232-1f23a, U+1f250-1f251, U+1f310, U+1f3a6, U+1f3b5-1f3b6, U+1f3bc, U+1f3e7, U+1f441, U+1f499-1f49c, U+1f49f-1f4a0, U+1f4a2, U+1f4ac-1f4ad, U+1f4b1-1f4b2, U+1f4b9, U+1f4db, U+1f4f2-1f4f6, U+1f500-1f50a, U+1f515, U+1f518-1f524, U+1f52f-1f53d, U+1f549, U+1f54e, U+1f5a4, U+1f5e8, U+1f5ef, U+1f6ab, U+1f6ad-1f6b1, U+1f6b3, U+1f6b7-1f6bc, U+1f6be, U+1f6c2-1f6c5, U+1f6d0-1f6d1, U+1f6d7, U+1f6dc, U+1f7e0-1f7eb, U+1f7f0, U+1f90d-1f90e, U+1f9e1, U+1fa75-1fa77, U+1faaf; - } - /* [3] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.3.woff2) format('woff2'); - unicode-range: U+231a-231b, U+2328, U+23f0-23f3, U+2602, U+260e, U+2692, U+2694, U+2696-2697, U+2699, U+26b0-26b1, U+26cf, U+26d1, U+26d3, U+2702, U+2709, U+270f, U+2712, U+fe0f, U+1f302, U+1f321, U+1f392-1f393, U+1f3a9, U+1f3bd, U+1f3ee, U+1f3f7, U+1f3fa, U+1f451-1f462, U+1f484, U+1f489-1f48a, U+1f48c-1f48e, U+1f4a1, U+1f4a3, U+1f4b0, U+1f4b3-1f4b8, U+1f4bb-1f4da, U+1f4dc-1f4f1, U+1f4ff, U+1f50b-1f514, U+1f516-1f517, U+1f526-1f529, U+1f52c-1f52e, U+1f550-1f567, U+1f56f-1f570, U+1f576, U+1f587, U+1f58a-1f58d, U+1f5a5, U+1f5a8, U+1f5b1-1f5b2, U+1f5c2-1f5c4, U+1f5d1-1f5d3, U+1f5dc-1f5de, U+1f5e1, U+1f5f3, U+1f6aa, U+1f6ac, U+1f6bd, U+1f6bf, U+1f6c1, U+1f6cb, U+1f6cd-1f6cf, U+1f6d2, U+1f6e0-1f6e1, U+1f6f0, U+1f97b-1f97f, U+1f9af, U+1f9ba, U+1f9e2-1f9e6, U+1f9ea-1f9ec, U+1f9ee-1f9f4, U+1f9f7-1f9ff, U+1fa71-1fa74, U+1fa79-1fa7b, U+1fa86, U+1fa91-1fa93, U+1fa96, U+1fa99-1faa0, U+1faa2-1faa7, U+1faaa-1faae; - } - /* [4] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.4.woff2) format('woff2'); - unicode-range: U+265f, U+26bd-26be, U+26f3, U+26f8, U+fe0f, U+1f004, U+1f0cf, U+1f380-1f384, U+1f386-1f38b, U+1f38d-1f391, U+1f396-1f397, U+1f399-1f39b, U+1f39e-1f39f, U+1f3a3-1f3a5, U+1f3a7-1f3a9, U+1f3ab-1f3b4, U+1f3b7-1f3bb, U+1f3bd-1f3c0, U+1f3c5-1f3c6, U+1f3c8-1f3c9, U+1f3cf-1f3d3, U+1f3f8-1f3f9, U+1f47e, U+1f4e2, U+1f4f7-1f4fd, U+1f52b, U+1f579, U+1f58c-1f58d, U+1f5bc, U+1f6f7, U+1f6f9, U+1f6fc, U+1f93f, U+1f941, U+1f945, U+1f947-1f94f, U+1f9e7-1f9e9, U+1f9f5-1f9f6, U+1fa70-1fa71, U+1fa80-1fa81, U+1fa83-1fa85, U+1fa87-1fa88, U+1fa94-1fa95, U+1fa97-1fa98, U+1faa1, U+1faa9; - } - /* [5] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.5.woff2) format('woff2'); - unicode-range: U+2693, U+26e9-26ea, U+26f1-26f2, U+26f4-26f5, U+26fa, U+26fd, U+2708, U+fe0f, U+1f301, U+1f303, U+1f306-1f307, U+1f309, U+1f310, U+1f3a0-1f3a2, U+1f3aa, U+1f3cd-1f3ce, U+1f3d5, U+1f3d7-1f3db, U+1f3df-1f3e6, U+1f3e8-1f3ed, U+1f3ef-1f3f0, U+1f488, U+1f492, U+1f4ba, U+1f54b-1f54d, U+1f5fa-1f5ff, U+1f680-1f6a2, U+1f6a4-1f6a8, U+1f6b2, U+1f6d1, U+1f6d5-1f6d6, U+1f6dd-1f6df, U+1f6e2-1f6e5, U+1f6e9, U+1f6eb-1f6ec, U+1f6f3-1f6f6, U+1f6f8, U+1f6fa-1f6fb, U+1f9bc-1f9bd, U+1f9ed, U+1f9f3, U+1fa7c; - } - /* [6] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.6.woff2) format('woff2'); - unicode-range: U+2615, U+fe0f, U+1f32d-1f330, U+1f336, U+1f33d, U+1f345-1f37f, U+1f382, U+1f52a, U+1f942-1f944, U+1f950-1f96f, U+1f99e, U+1f9aa, U+1f9c0-1f9cb, U+1fad0-1fadb; - } - /* [7] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.7.woff2) format('woff2'); - unicode-range: U+200d, U+2600-2601, U+2603-2604, U+2614, U+2618, U+26a1, U+26c4-26c5, U+26c8, U+26f0, U+2728, U+2744, U+2b1b, U+2b50, U+fe0f, U+1f300, U+1f304-1f305, U+1f308, U+1f30a-1f30f, U+1f311-1f321, U+1f324-1f32c, U+1f331-1f335, U+1f337-1f33c, U+1f33e-1f344, U+1f3d4, U+1f3d6, U+1f3dc-1f3de, U+1f3f5, U+1f400-1f43f, U+1f490, U+1f4a7, U+1f4ab, U+1f4ae, U+1f525, U+1f54a, U+1f573, U+1f577-1f578, U+1f648-1f64a, U+1f940, U+1f980-1f9ae, U+1f9ba, U+1fa90, U+1faa8, U+1fab0-1fabd, U+1fabf, U+1face-1facf, U+1fae7; - } - /* [8] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.8.woff2) format('woff2'); - unicode-range: U+200d, U+2640, U+2642, U+2695-2696, U+26f7, U+26f9, U+2708, U+2764, U+fe0f, U+1f33e, U+1f373, U+1f37c, U+1f384-1f385, U+1f393, U+1f3a4, U+1f3a8, U+1f3c2-1f3c4, U+1f3c7, U+1f3ca-1f3cc, U+1f3eb, U+1f3ed, U+1f3fb-1f3ff, U+1f466-1f478, U+1f47c, U+1f481-1f483, U+1f486-1f487, U+1f48b, U+1f48f, U+1f491, U+1f4bb-1f4bc, U+1f527, U+1f52c, U+1f574-1f575, U+1f57a, U+1f645-1f647, U+1f64b, U+1f64d-1f64e, U+1f680, U+1f692, U+1f6a3, U+1f6b4-1f6b6, U+1f6c0, U+1f6cc, U+1f91d, U+1f926, U+1f930-1f931, U+1f934-1f93a, U+1f93c-1f93e, U+1f977, U+1f9af-1f9b3, U+1f9b8-1f9b9, U+1f9bc-1f9bd, U+1f9cc-1f9cf, U+1f9d1-1f9df, U+1fa82, U+1fac3-1fac5; - } - /* [9] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.9.woff2) format('woff2'); - unicode-range: U+200d, U+261d, U+2620, U+2639-263a, U+2665, U+270a-270d, U+2728, U+2763-2764, U+2b50, U+fe0f, U+1f31a-1f31f, U+1f32b, U+1f383, U+1f389, U+1f3fb-1f3ff, U+1f440-1f450, U+1f463-1f465, U+1f479-1f47b, U+1f47d-1f480, U+1f485, U+1f48b-1f48c, U+1f493-1f49f, U+1f4a4-1f4a6, U+1f4a8-1f4ab, U+1f4af, U+1f525, U+1f573, U+1f590, U+1f595-1f596, U+1f5a4, U+1f5e3, U+1f600-1f644, U+1f648-1f64a, U+1f64c, U+1f64f, U+1f90c-1f925, U+1f927-1f92f, U+1f932-1f933, U+1f970-1f976, U+1f978-1f97a, U+1f9a0, U+1f9b4-1f9b7, U+1f9bb, U+1f9be-1f9bf, U+1f9d0, U+1f9e0-1f9e1, U+1fa75-1fa79, U+1fac0-1fac2, U+1fae0-1fae6, U+1fae8, U+1faf0-1faf8; - } - /* [10] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.10.woff2) format('woff2'); - unicode-range: U+200d, U+2194-2195, U+2640, U+2642, U+26d3, U+27a1, U+fe0f, U+1f344, U+1f34b, U+1f3c3, U+1f3fb-1f3ff, U+1f426, U+1f468-1f469, U+1f4a5, U+1f525, U+1f642, U+1f6b6, U+1f7e9, U+1f7eb, U+1f9af, U+1f9bc-1f9bd, U+1f9ce, U+1f9d1-1f9d2; - } - /* [11] */ - @font-face { - font-family: 'Noto Color Emoji'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.11.woff2) format('woff2'); - unicode-range: U+1fa89, U+1fa8f, U+1fabe, U+1fac6, U+1fadc, U+1fadf, U+1fae9; - } - /* [0] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.0.woff2) format('woff2'); - unicode-range: U+1f1e6-1f1ff; - } - /* [1] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.1.woff2) format('woff2'); - unicode-range: U+200d, U+2620, U+26a7, U+fe0f, U+1f308, U+1f38c, U+1f3c1, U+1f3f3-1f3f4, U+1f6a9, U+e0062-e0063, U+e0065, U+e0067, U+e006c, U+e006e, U+e0073-e0074, U+e0077, U+e007f; - } - /* [2] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.2.woff2) format('woff2'); - unicode-range: U+23, U+2a, U+30-39, U+a9, U+ae, U+200d, U+203c, U+2049, U+20e3, U+2122, U+2139, U+2194-2199, U+21a9-21aa, U+23cf, U+23e9-23ef, U+23f8-23fa, U+24c2, U+25aa-25ab, U+25b6, U+25c0, U+25fb-25fe, U+2611, U+2622-2623, U+2626, U+262a, U+262e-262f, U+2638, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267b, U+267e-267f, U+2695, U+269b-269c, U+26a0, U+26a7, U+26aa-26ab, U+26ce, U+26d4, U+2705, U+2714, U+2716, U+271d, U+2721, U+2733-2734, U+2747, U+274c, U+274e, U+2753-2755, U+2757, U+2764, U+2795-2797, U+27a1, U+27b0, U+27bf, U+2934-2935, U+2b05-2b07, U+2b1b-2b1c, U+2b55, U+3030, U+303d, U+3297, U+3299, U+fe0f, U+1f170-1f171, U+1f17e-1f17f, U+1f18e, U+1f191-1f19a, U+1f201-1f202, U+1f21a, U+1f22f, U+1f232-1f23a, U+1f250-1f251, U+1f310, U+1f3a6, U+1f3b5-1f3b6, U+1f3bc, U+1f3e7, U+1f441, U+1f499-1f49c, U+1f49f-1f4a0, U+1f4a2, U+1f4ac-1f4ad, U+1f4b1-1f4b2, U+1f4b9, U+1f4db, U+1f4f2-1f4f6, U+1f500-1f50a, U+1f515, U+1f518-1f524, U+1f52f-1f53d, U+1f549, U+1f54e, U+1f5a4, U+1f5e8, U+1f5ef, U+1f6ab, U+1f6ad-1f6b1, U+1f6b3, U+1f6b7-1f6bc, U+1f6be, U+1f6c2-1f6c5, U+1f6d0-1f6d1, U+1f6d7, U+1f6dc, U+1f7e0-1f7eb, U+1f7f0, U+1f90d-1f90e, U+1f9e1, U+1fa75-1fa77, U+1faaf; - } - /* [3] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.3.woff2) format('woff2'); - unicode-range: U+231a-231b, U+2328, U+23f0-23f3, U+2602, U+260e, U+2692, U+2694, U+2696-2697, U+2699, U+26b0-26b1, U+26cf, U+26d1, U+26d3, U+2702, U+2709, U+270f, U+2712, U+fe0f, U+1f302, U+1f321, U+1f392-1f393, U+1f3a9, U+1f3bd, U+1f3ee, U+1f3f7, U+1f3fa, U+1f451-1f462, U+1f484, U+1f489-1f48a, U+1f48c-1f48e, U+1f4a1, U+1f4a3, U+1f4b0, U+1f4b3-1f4b8, U+1f4bb-1f4da, U+1f4dc-1f4f1, U+1f4ff, U+1f50b-1f514, U+1f516-1f517, U+1f526-1f529, U+1f52c-1f52e, U+1f550-1f567, U+1f56f-1f570, U+1f576, U+1f587, U+1f58a-1f58d, U+1f5a5, U+1f5a8, U+1f5b1-1f5b2, U+1f5c2-1f5c4, U+1f5d1-1f5d3, U+1f5dc-1f5de, U+1f5e1, U+1f5f3, U+1f6aa, U+1f6ac, U+1f6bd, U+1f6bf, U+1f6c1, U+1f6cb, U+1f6cd-1f6cf, U+1f6d2, U+1f6e0-1f6e1, U+1f6f0, U+1f97b-1f97f, U+1f9af, U+1f9ba, U+1f9e2-1f9e6, U+1f9ea-1f9ec, U+1f9ee-1f9f4, U+1f9f7-1f9ff, U+1fa71-1fa74, U+1fa79-1fa7b, U+1fa86, U+1fa91-1fa93, U+1fa96, U+1fa99-1faa0, U+1faa2-1faa7, U+1faaa-1faae; - } - /* [4] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.4.woff2) format('woff2'); - unicode-range: U+265f, U+26bd-26be, U+26f3, U+26f8, U+fe0f, U+1f004, U+1f0cf, U+1f380-1f384, U+1f386-1f38b, U+1f38d-1f391, U+1f396-1f397, U+1f399-1f39b, U+1f39e-1f39f, U+1f3a3-1f3a5, U+1f3a7-1f3a9, U+1f3ab-1f3b4, U+1f3b7-1f3bb, U+1f3bd-1f3c0, U+1f3c5-1f3c6, U+1f3c8-1f3c9, U+1f3cf-1f3d3, U+1f3f8-1f3f9, U+1f47e, U+1f4e2, U+1f4f7-1f4fd, U+1f52b, U+1f579, U+1f58c-1f58d, U+1f5bc, U+1f6f7, U+1f6f9, U+1f6fc, U+1f93f, U+1f941, U+1f945, U+1f947-1f94f, U+1f9e7-1f9e9, U+1f9f5-1f9f6, U+1fa70-1fa71, U+1fa80-1fa81, U+1fa83-1fa85, U+1fa87-1fa88, U+1fa94-1fa95, U+1fa97-1fa98, U+1faa1, U+1faa9; - } - /* [5] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.5.woff2) format('woff2'); - unicode-range: U+2693, U+26e9-26ea, U+26f1-26f2, U+26f4-26f5, U+26fa, U+26fd, U+2708, U+fe0f, U+1f301, U+1f303, U+1f306-1f307, U+1f309, U+1f310, U+1f3a0-1f3a2, U+1f3aa, U+1f3cd-1f3ce, U+1f3d5, U+1f3d7-1f3db, U+1f3df-1f3e6, U+1f3e8-1f3ed, U+1f3ef-1f3f0, U+1f488, U+1f492, U+1f4ba, U+1f54b-1f54d, U+1f5fa-1f5ff, U+1f680-1f6a2, U+1f6a4-1f6a8, U+1f6b2, U+1f6d1, U+1f6d5-1f6d6, U+1f6dd-1f6df, U+1f6e2-1f6e5, U+1f6e9, U+1f6eb-1f6ec, U+1f6f3-1f6f6, U+1f6f8, U+1f6fa-1f6fb, U+1f9bc-1f9bd, U+1f9ed, U+1f9f3, U+1fa7c; - } - /* [6] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.6.woff2) format('woff2'); - unicode-range: U+2615, U+fe0f, U+1f32d-1f330, U+1f336, U+1f33d, U+1f345-1f37f, U+1f382, U+1f52a, U+1f942-1f944, U+1f950-1f96f, U+1f99e, U+1f9aa, U+1f9c0-1f9cb, U+1fad0-1fadb; - } - /* [7] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.7.woff2) format('woff2'); - unicode-range: U+200d, U+2600-2601, U+2603-2604, U+2614, U+2618, U+26a1, U+26c4-26c5, U+26c8, U+26f0, U+2728, U+2744, U+2b1b, U+2b50, U+fe0f, U+1f300, U+1f304-1f305, U+1f308, U+1f30a-1f30f, U+1f311-1f321, U+1f324-1f32c, U+1f331-1f335, U+1f337-1f33c, U+1f33e-1f344, U+1f3d4, U+1f3d6, U+1f3dc-1f3de, U+1f3f5, U+1f400-1f43f, U+1f490, U+1f4a7, U+1f4ab, U+1f4ae, U+1f525, U+1f54a, U+1f573, U+1f577-1f578, U+1f648-1f64a, U+1f940, U+1f980-1f9ae, U+1f9ba, U+1fa90, U+1faa8, U+1fab0-1fabd, U+1fabf, U+1face-1facf, U+1fae7; - } - /* [8] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.8.woff2) format('woff2'); - unicode-range: U+200d, U+2640, U+2642, U+2695-2696, U+26f7, U+26f9, U+2708, U+2764, U+fe0f, U+1f33e, U+1f373, U+1f37c, U+1f384-1f385, U+1f393, U+1f3a4, U+1f3a8, U+1f3c2-1f3c4, U+1f3c7, U+1f3ca-1f3cc, U+1f3eb, U+1f3ed, U+1f3fb-1f3ff, U+1f466-1f478, U+1f47c, U+1f481-1f483, U+1f486-1f487, U+1f48b, U+1f48f, U+1f491, U+1f4bb-1f4bc, U+1f527, U+1f52c, U+1f574-1f575, U+1f57a, U+1f645-1f647, U+1f64b, U+1f64d-1f64e, U+1f680, U+1f692, U+1f6a3, U+1f6b4-1f6b6, U+1f6c0, U+1f6cc, U+1f91d, U+1f926, U+1f930-1f931, U+1f934-1f93a, U+1f93c-1f93e, U+1f977, U+1f9af-1f9b3, U+1f9b8-1f9b9, U+1f9bc-1f9bd, U+1f9cc-1f9cf, U+1f9d1-1f9df, U+1fa82, U+1fac3-1fac5; - } - /* [9] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.9.woff2) format('woff2'); - unicode-range: U+200d, U+261d, U+2620, U+2639-263a, U+2665, U+270a-270d, U+2728, U+2763-2764, U+2b50, U+fe0f, U+1f31a-1f31f, U+1f32b, U+1f383, U+1f389, U+1f3fb-1f3ff, U+1f440-1f450, U+1f463-1f465, U+1f479-1f47b, U+1f47d-1f480, U+1f485, U+1f48b-1f48c, U+1f493-1f49f, U+1f4a4-1f4a6, U+1f4a8-1f4ab, U+1f4af, U+1f525, U+1f573, U+1f590, U+1f595-1f596, U+1f5a4, U+1f5e3, U+1f600-1f644, U+1f648-1f64a, U+1f64c, U+1f64f, U+1f90c-1f925, U+1f927-1f92f, U+1f932-1f933, U+1f970-1f976, U+1f978-1f97a, U+1f9a0, U+1f9b4-1f9b7, U+1f9bb, U+1f9be-1f9bf, U+1f9d0, U+1f9e0-1f9e1, U+1fa75-1fa79, U+1fac0-1fac2, U+1fae0-1fae6, U+1fae8, U+1faf0-1faf8; - } - /* [10] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.10.woff2) format('woff2'); - unicode-range: U+200d, U+2194-2195, U+2640, U+2642, U+26d3, U+27a1, U+fe0f, U+1f344, U+1f34b, U+1f3c3, U+1f3fb-1f3ff, U+1f426, U+1f468-1f469, U+1f4a5, U+1f525, U+1f642, U+1f6b6, U+1f7e9, U+1f7eb, U+1f9af, U+1f9bc-1f9bd, U+1f9ce, U+1f9d1-1f9d2; - } - /* [11] */ - @font-face { - font-family: 'Noto Emoji'; - font-style: normal; - font-weight: 300 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/notoemoji/v51/bMrymSyK7YY-MEu6aWjPFMHQUnEOtg_Uy9ZzkQ.11.woff2) format('woff2'); - unicode-range: U+1fa89, U+1fa8f, U+1fabe, U+1fac6, U+1fadc, U+1fadf, U+1fae9; - } - body { - --google-font-color-notocoloremoji:colrv1; - } \ No newline at end of file diff --git a/src/games/ai-story/assets/favicon.ico b/src/games/ai-story/assets/favicon.ico deleted file mode 100644 index f18ba1e..0000000 Binary files a/src/games/ai-story/assets/favicon.ico and /dev/null differ diff --git a/src/games/ai-story/assets/message.mp3 b/src/games/ai-story/assets/message.mp3 deleted file mode 100644 index 673180f..0000000 Binary files a/src/games/ai-story/assets/message.mp3 and /dev/null differ diff --git a/src/games/ai-story/assets/pwa_icon.png b/src/games/ai-story/assets/pwa_icon.png deleted file mode 100644 index 4a23727..0000000 Binary files a/src/games/ai-story/assets/pwa_icon.png and /dev/null differ diff --git a/src/games/ai-story/assets/style.css b/src/games/ai-story/assets/style.css deleted file mode 100644 index d48957d..0000000 --- a/src/games/ai-story/assets/style.css +++ /dev/null @@ -1,162 +0,0 @@ -:root { - --backgroundColorDark: rgba(0, 0, 0, 0.3); - --backgroundColor: rgba(51, 51, 51, 0.9); - --color: #DCDCD2; - --italicColor: #AFAFAF; - --quoteColor: #D4E5FF; - --green: #AFAFAF; - --red: #7F0000; - --green: #007F00; - --brightRed: #DD0000; - --brightGreen: #00DD00; - --shadeColor: rgba(0, 128, 128, 0.3); - - --border: 1px solid var(--color); - --border-radius: 4px; - - --emojiFont: "Noto Emoji", sans-serif; - --emojiColorFont: "Noto Color Emoji", sans-serif; -} - -* { - scrollbar-width: thin; - scrollbar-color: var(--color) transparent; -} - -textarea, -input, -select { - color: var(--color); - border: var(--border); - background-color: var(--backgroundColorDark); - font-size: 1em; - font-family: sans-serif; - outline: none; -} - -option, optgroup { - background-color: var(--backgroundColor); -} - -textarea { - resize: vertical; - width: 100%; - min-height: 100px; - padding: 4px; - line-height: 1.5; -} - -button { - border: var(--border); - background-color: var(--backgroundColor); - color: var(--color); - cursor: pointer; - user-select: none; - - &.disabled { - pointer-events: none; - opacity: 0.5; - } - - &.icon { - font-family: var(--emojiFont); - font-size: 20px; - border: none; - background: none; - padding: 0; - - &.color { - font-family: var(--emojiColorFont); - } - } -} - -body { - color: var(--color); - width: 100dvw; - height: 100dvh; - font-size: 16px; - line-height: 1.5; - touch-action: none; - - .root { - background-size: cover; - background-position: center; - background-repeat: no-repeat; - width: 100%; - height: 100%; - - display: flex; - flex-direction: row; - justify-content: center; - background-color: var(--backgroundColor); - } - - .app { - display: flex; - flex-direction: column; - background-color: var(--backgroundColor); - - width: 100%; - max-width: 1200px; - height: 100%; - max-height: 100dvh; - - >.chat { - display: flex; - flex-direction: column; - height: 100%; - flex-grow: 1; - max-width: 100%; - overflow-x: hidden; - overflow-y: auto; - scrollbar-width: thin; - scrollbar-color: var(--color) transparent; - border: var(--border); - border-bottom: none; - border-top: none; - } - - >.chat-input { - display: flex; - flex-direction: row; - height: auto; - width: 100%; - - >textarea { - min-height: 48px; - resize: none; - background-color: var(--backgroundColor); - } - } - } -} - -.ace_editor { - background-color: var(--backgroundColorDark) !important; - border: var(--border) !important; -} - -@keyframes swipe-from-left { - 0% { - position: relative; - left: -100%; - } - - 100% { - position: relative; - left: 0; - } -} - -@keyframes swipe-from-right { - 0% { - position: relative; - right: -100%; - } - - 100% { - position: relative; - right: 0; - } -} \ No newline at end of file diff --git a/src/games/ai-story/components/ace.tsx b/src/games/ai-story/components/ace.tsx deleted file mode 100644 index e5af98f..0000000 --- a/src/games/ai-story/components/ace.tsx +++ /dev/null @@ -1,61 +0,0 @@ - -import { useEffect, useMemo, useRef } from "preact/hooks"; -import ace from "ace-builds"; -import { useIsVisible } from "@common/hooks/useIsVisible"; - -import "ace-builds/src-noconflict/mode-django"; -import "ace-builds/src-noconflict/theme-terminal"; - -interface IAceProps { - value: string; - onInput: (e: InputEvent | string) => void; -} - -export const Ace = ({ value, onInput }: IAceProps) => { - const ref = useRef(null); - const isVisible = useIsVisible(ref); - - const editor = useMemo(() => { - if (ref.current) { - const e = ace.edit(ref.current, { - theme: 'ace/theme/terminal', - mode: 'ace/mode/django', - showGutter: false, - showPrintMargin: false, - highlightActiveLine: false, - displayIndentGuides: false, - fontSize: 16, - maxLines: Infinity, - tabSize: 2, - useSoftTabs: true, - wrap: "free", - }); - return e; - } - }, [isVisible]); - - useEffect(() => { - if (editor) { - if (editor.getValue() !== value) { - const pos = editor.getCursorPosition(); - editor.setValue(value); - editor.selection.clearSelection(); - editor.moveCursorToPosition(pos); - } - } - }, [editor, value]); - - useEffect(() => { - if (onInput && editor) { - const e = editor; - const handler = () => onInput(e.getValue()); - - e.on('input', handler); - return () => e.off('input', handler); - } - }, [editor, onInput]); - - return ( -
- ); -} \ No newline at end of file diff --git a/src/games/ai-story/components/app.tsx b/src/games/ai-story/components/app.tsx deleted file mode 100644 index 4d4aa76..0000000 --- a/src/games/ai-story/components/app.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Header } from "./header/header"; -import { Chat } from "./chat"; -import { Input } from "./input"; - -import bgImage from '../assets/bg.jpg'; - -export const App = () => { - return ( -
-
-
- - -
-
- ); -}; diff --git a/src/games/ai-story/components/autoTextarea.tsx b/src/games/ai-story/components/autoTextarea.tsx deleted file mode 100644 index ac69a93..0000000 --- a/src/games/ai-story/components/autoTextarea.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useEffect, useRef } from "preact/hooks"; -import type { JSX } from "preact/jsx-runtime" - -import { useIsVisible } from '@common/hooks/useIsVisible'; -import { DOMTools } from "../tools/dom"; - -export const AutoTextarea = (props: JSX.HTMLAttributes) => { - const { value } = props; - const ref = useRef(null); - const isVisible = useIsVisible(ref); - - useEffect(() => { - if (ref.current && isVisible) { - const area = ref.current; - - const { height } = DOMTools.calculateNodeHeight(area); - area.style.height = `${height}px`; - } - }, [value, isVisible]); - - return