diff --git a/.gitignore b/.gitignore index 2e161f5..97ee043 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules .svelte-kit build +offline-dist +release .env .env.* !.env.example diff --git a/offline/OfflineApp.svelte b/offline/OfflineApp.svelte new file mode 100644 index 0000000..15d8f3a --- /dev/null +++ b/offline/OfflineApp.svelte @@ -0,0 +1,127 @@ + + + + + + + diff --git a/offline/chart-loader.ts b/offline/chart-loader.ts new file mode 100644 index 0000000..33a1208 --- /dev/null +++ b/offline/chart-loader.ts @@ -0,0 +1,24 @@ +import type { ExactSongChart } from '$lib/engine/index.ts'; + +const chartSources = import.meta.glob('../static/data/charts/*.json', { + eager: true, + query: '?raw', + import: 'default' +}) as Record; + +const sourceByFilename = new Map( + Object.entries(chartSources).map(([path, source]) => [path.split('/').at(-1) ?? path, source]) +); +const parsedCharts = new Map(); + +export async function loadExactSongChart(path: string): Promise { + const filename = path.split('/').at(-1) ?? path; + const cached = parsedCharts.get(filename); + if (cached) return cached; + + const source = sourceByFilename.get(filename); + if (!source) throw new Error(`포함된 채보 데이터를 찾지 못했습니다: ${filename}`); + const chart = JSON.parse(source) as ExactSongChart; + parsedCharts.set(filename, chart); + return chart; +} diff --git a/offline/index.html b/offline/index.html new file mode 100644 index 0000000..bbcf80f --- /dev/null +++ b/offline/index.html @@ -0,0 +1,13 @@ + + + + + + + 홀로도리 계산기 + + +
+ + + diff --git a/offline/main.ts b/offline/main.ts new file mode 100644 index 0000000..ba93d51 --- /dev/null +++ b/offline/main.ts @@ -0,0 +1,7 @@ +import { mount } from 'svelte'; +import App from './OfflineApp.svelte'; +import '../src/lib/styles/layout-tokens.css'; + +mount(App, { + target: document.getElementById('app')! +}); diff --git a/offline/page-store.ts b/offline/page-store.ts new file mode 100644 index 0000000..a83cce3 --- /dev/null +++ b/offline/page-store.ts @@ -0,0 +1,11 @@ +import { writable } from 'svelte/store'; + +function pageValue(pathname: string) { + return { url: new URL(pathname, 'https://offline.holodori.local') }; +} + +export const page = writable(pageValue('/')); + +export function setOfflinePage(pathname: string) { + page.set(pageValue(pathname)); +} diff --git a/package.json b/package.json index 7687873..c9af01c 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "scripts": { "dev": "vite dev --host 0.0.0.0 --port 5173", "build": "vite build", + "build:offline": "vite build --config vite.offline.config.ts && node scripts/finalize-offline-build.mjs", + "package:offline": "npm run build:offline && powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package-offline.ps1", "preview": "vite preview --host 0.0.0.0", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "test:engine": "node --test src/lib/engine/*.test.ts", diff --git a/scripts/finalize-offline-build.mjs b/scripts/finalize-offline-build.mjs new file mode 100644 index 0000000..eedff36 --- /dev/null +++ b/scripts/finalize-offline-build.mjs @@ -0,0 +1,56 @@ +import { readFile, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +const output = path.resolve('offline-dist'); +const htmlPath = path.join(output, 'index.html'); +const scriptPath = path.join(output, 'assets', 'app.js'); +const stylePath = path.join(output, 'assets', 'app.css'); +const packageJson = JSON.parse(await readFile(path.resolve('package.json'), 'utf8')); + +let html = await readFile(htmlPath, 'utf8'); +html = html.replace(/