Lint and so on
This commit is contained in:
2
.prettierignore
Normal file
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
||||
|
||||
@@ -42,6 +42,6 @@ If you have state that's important to retain within a component, consider creati
|
||||
```ts
|
||||
// store.ts
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
import { writable } from 'svelte/store';
|
||||
export default writable(0);
|
||||
```
|
||||
|
||||
40
eslint.config.js
Normal file
40
eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import js from '@eslint/js';
|
||||
import ts from 'typescript-eslint';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import globals from 'globals';
|
||||
|
||||
export default ts.config(
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
{
|
||||
languageOptions: {
|
||||
globals: { ...globals.browser, ...globals.node },
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte', '**/*.svelte.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser,
|
||||
extraFileExtensions: ['.svelte'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte.ts'],
|
||||
languageOptions: {
|
||||
parser: ts.parser,
|
||||
},
|
||||
},
|
||||
{ ignores: ['dist/'] },
|
||||
);
|
||||
20
index.html
20
index.html
@@ -1,13 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>buboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>buboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
87
package.json
87
package.json
@@ -1,41 +1,50 @@
|
||||
{
|
||||
"name": "buboard",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||
"@tsconfig/svelte": "^5.0.6",
|
||||
"@types/node": "^24.10.1",
|
||||
"svelte": "^5.43.8",
|
||||
"svelte-check": "^4.3.4",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "npm:rolldown-vite@7.2.5"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite": "npm:rolldown-vite@7.2.5"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.10.0",
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.11",
|
||||
"@codemirror/language": "^6.11.3",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@codemirror/view": "^6.38.8",
|
||||
"@lezer/highlight": "^1.2.3",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-svelte": "^0.555.0"
|
||||
}
|
||||
"name": "buboard",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||
"@tsconfig/svelte": "^5.0.6",
|
||||
"@types/node": "^24.10.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.13.0",
|
||||
"globals": "^16.5.0",
|
||||
"prettier": "^3.7.4",
|
||||
"svelte": "^5.43.8",
|
||||
"svelte-check": "^4.3.4",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.48.1",
|
||||
"vite": "npm:rolldown-vite@7.2.5"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite": "npm:rolldown-vite@7.2.5"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.10.0",
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.11",
|
||||
"@codemirror/language": "^6.11.3",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@codemirror/view": "^6.38.8",
|
||||
"@lezer/highlight": "^1.2.3",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-svelte": "^0.555.0"
|
||||
}
|
||||
}
|
||||
|
||||
997
pnpm-lock.yaml
generated
997
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html `<style>${appState.manifest.appCss}</style>`}
|
||||
</svelte:head>
|
||||
|
||||
@@ -65,7 +66,7 @@
|
||||
<button class="show-ui" onclick={() => (interfaceHidden = false)} title="Show interface">
|
||||
<Eye size={14} />
|
||||
</button>
|
||||
{#each ['1', '2', '3', '4', '5', '6', '7', '8', '9'] as key}
|
||||
{#each ['1', '2', '3', '4', '5', '6', '7', '8', '9'] as key (key)}
|
||||
<button
|
||||
class="flag"
|
||||
class:filled={appState.hasFlag(key)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@font-face {
|
||||
font-family: 'Departure Mono';
|
||||
src: url('/fonts/DepartureMono-Regular.woff2') format('woff2'),
|
||||
src:
|
||||
url('/fonts/DepartureMono-Regular.woff2') format('woff2'),
|
||||
url('/fonts/DepartureMono-Regular.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import type { Item } from './types';
|
||||
import { state as appState } from './state.svelte';
|
||||
import { calculateCenterOffset, constrainToAspectRatio } from './geometry';
|
||||
@@ -11,7 +12,7 @@
|
||||
let isResizing = $state(false);
|
||||
let isRotating = $state(false);
|
||||
let dragStart = { x: 0, y: 0, itemX: 0, itemY: 0 };
|
||||
let dragStartPositions: Map<string, { x: number; y: number }> = new Map();
|
||||
let dragStartPositions: SvelteMap<string, { x: number; y: number }> = new SvelteMap();
|
||||
let resizeStart = { x: 0, y: 0, width: 0, height: 0, itemX: 0, itemY: 0, corner: '', aspectRatio: 1 };
|
||||
let rotateStart = { angle: 0, startAngle: 0 };
|
||||
|
||||
@@ -55,7 +56,7 @@
|
||||
itemX: item.x,
|
||||
itemY: item.y
|
||||
};
|
||||
dragStartPositions = new Map();
|
||||
dragStartPositions = new SvelteMap();
|
||||
for (const id of appState.selectedIds) {
|
||||
const selectedItem = appState.getItem(id);
|
||||
if (selectedItem) {
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
const x = -state.viewport.x / state.viewport.zoom + 400;
|
||||
const y = -state.viewport.y / state.viewport.zoom + 300;
|
||||
|
||||
const img = new Image();
|
||||
const img = document.createElement('img');
|
||||
img.onload = () => {
|
||||
state.addAsset(assetId, { blob: file, url, filename: file.name });
|
||||
state.addItem({
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<span class="app-name">Buboard</span>
|
||||
<Palette />
|
||||
<div class="flags">
|
||||
{#each flagKeys as key}
|
||||
{#each flagKeys as key (key)}
|
||||
<button
|
||||
class="flag"
|
||||
class:filled={state.hasFlag(key)}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function calculateCenterOffset(
|
||||
corner: string,
|
||||
deltaWidth: number,
|
||||
deltaHeight: number,
|
||||
rotation: number
|
||||
rotation: number,
|
||||
): Point {
|
||||
const rad = (rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(rad);
|
||||
@@ -24,14 +24,14 @@ export function calculateCenterOffset(
|
||||
|
||||
return {
|
||||
x: localDx * cos - localDy * sin,
|
||||
y: localDx * sin + localDy * cos
|
||||
y: localDx * sin + localDy * cos,
|
||||
};
|
||||
}
|
||||
|
||||
export function constrainToAspectRatio(
|
||||
newWidth: number,
|
||||
newHeight: number,
|
||||
aspectRatio: number
|
||||
aspectRatio: number,
|
||||
): { width: number; height: number } {
|
||||
const newRatio = newWidth / newHeight;
|
||||
|
||||
@@ -47,13 +47,13 @@ export function detectRotationCorner(
|
||||
localY: number,
|
||||
halfWidth: number,
|
||||
halfHeight: number,
|
||||
zoneRadius: number
|
||||
zoneRadius: number,
|
||||
): string | null {
|
||||
const corners: Record<string, Point> = {
|
||||
nw: { x: -halfWidth, y: -halfHeight },
|
||||
ne: { x: halfWidth, y: -halfHeight },
|
||||
sw: { x: -halfWidth, y: halfHeight },
|
||||
se: { x: halfWidth, y: halfHeight }
|
||||
se: { x: halfWidth, y: halfHeight },
|
||||
};
|
||||
|
||||
const isInsideBounds =
|
||||
@@ -71,8 +71,10 @@ export function detectRotationCorner(
|
||||
|
||||
if (dist > zoneRadius || dist < 3) continue;
|
||||
|
||||
const isOutwardX = (name.includes('w') && dx < 0) || (name.includes('e') && dx > 0);
|
||||
const isOutwardY = (name.includes('n') && dy < 0) || (name.includes('s') && dy > 0);
|
||||
const isOutwardX =
|
||||
(name.includes('w') && dx < 0) || (name.includes('e') && dx > 0);
|
||||
const isOutwardY =
|
||||
(name.includes('n') && dy < 0) || (name.includes('s') && dy > 0);
|
||||
|
||||
if (isOutwardX || isOutwardY) return name;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ import JSZip from 'jszip';
|
||||
import type { Manifest, AssetStore } from './types';
|
||||
import { state } from './state.svelte';
|
||||
|
||||
export async function exportBoard(): Promise<{ success: boolean; error?: string }> {
|
||||
export async function exportBoard(): Promise<{
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const zip = new JSZip();
|
||||
const assetsFolder = zip.folder('assets');
|
||||
@@ -12,7 +15,7 @@ export async function exportBoard(): Promise<{ success: boolean; error?: string
|
||||
version: 1,
|
||||
items: state.manifest.items.map((item) => ({ ...item })),
|
||||
sharedCss: state.manifest.sharedCss,
|
||||
appCss: state.manifest.appCss
|
||||
appCss: state.manifest.appCss,
|
||||
};
|
||||
|
||||
for (const item of exportManifest.items) {
|
||||
@@ -31,7 +34,7 @@ export async function exportBoard(): Promise<{ success: boolean; error?: string
|
||||
const blob = await zip.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: { level: 9 }
|
||||
compressionOptions: { level: 9 },
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
@@ -41,28 +44,35 @@ export async function exportBoard(): Promise<{ success: boolean; error?: string
|
||||
URL.revokeObjectURL(url);
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, error: e instanceof Error ? e.message : 'Export failed' };
|
||||
return {
|
||||
success: false,
|
||||
error: e instanceof Error ? e.message : 'Export failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function importBoard(file: File): Promise<{ success: boolean; error?: string }> {
|
||||
export async function importBoard(
|
||||
file: File,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const zip = await JSZip.loadAsync(file);
|
||||
|
||||
const manifestFile = zip.file('manifest.json');
|
||||
if (!manifestFile) throw new Error('Invalid .bub file: missing manifest.json');
|
||||
if (!manifestFile)
|
||||
throw new Error('Invalid .bub file: missing manifest.json');
|
||||
|
||||
const manifestJson = await manifestFile.async('string');
|
||||
const raw = JSON.parse(manifestJson);
|
||||
|
||||
if (raw.version !== 1) throw new Error(`Unsupported manifest version: ${raw.version}`);
|
||||
if (raw.version !== 1)
|
||||
throw new Error(`Unsupported manifest version: ${raw.version}`);
|
||||
|
||||
const manifest: Manifest = {
|
||||
version: 1,
|
||||
items: raw.items,
|
||||
sharedCss: raw.sharedCss ?? '',
|
||||
appCss: raw.appCss ?? '',
|
||||
flags: raw.flags ?? {}
|
||||
flags: raw.flags ?? {},
|
||||
};
|
||||
|
||||
const assets: AssetStore = {};
|
||||
@@ -70,7 +80,9 @@ export async function importBoard(file: File): Promise<{ success: boolean; error
|
||||
|
||||
for (const item of manifest.items) {
|
||||
if (item.assetId) {
|
||||
const assetFiles = zip.folder('assets')?.file(new RegExp(`^${item.assetId}\\.`));
|
||||
const assetFiles = zip
|
||||
.folder('assets')
|
||||
?.file(new RegExp(`^${item.assetId}\\.`));
|
||||
if (assetFiles && assetFiles.length > 0) {
|
||||
const assetFile = assetFiles[0];
|
||||
const blob = await assetFile.async('blob');
|
||||
@@ -92,6 +104,9 @@ export async function importBoard(file: File): Promise<{ success: boolean; error
|
||||
state.load(manifest, assets);
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, error: e instanceof Error ? e.message : 'Import failed' };
|
||||
return {
|
||||
success: false,
|
||||
error: e instanceof Error ? e.message : 'Import failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Item, Manifest, Asset, AssetStore, Viewport, PositionFlag } from './types';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
import type { Item, Manifest, Asset, AssetStore, Viewport } from './types';
|
||||
|
||||
const STORAGE_KEY = 'buboard';
|
||||
|
||||
@@ -68,11 +69,11 @@ function createState() {
|
||||
items: [],
|
||||
sharedCss: DEFAULT_SHARED_CSS,
|
||||
appCss: DEFAULT_APP_CSS,
|
||||
flags: {}
|
||||
flags: {},
|
||||
});
|
||||
let assets = $state<AssetStore>({});
|
||||
let viewport = $state<Viewport>({ x: 0, y: 0, zoom: 1 });
|
||||
let selectedIds = $state<Set<string>>(new Set());
|
||||
let selectedIds = new SvelteSet<string>();
|
||||
let editingId = $state<string | null>(null);
|
||||
let editingGlobal = $state<boolean>(false);
|
||||
let focusedId = $state<string | null>(null);
|
||||
@@ -81,8 +82,10 @@ function createState() {
|
||||
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let animationId: number | null = null;
|
||||
|
||||
let maxZIndex = $derived(
|
||||
manifest.items.length > 0 ? Math.max(...manifest.items.map((i) => i.zIndex)) : 0
|
||||
const maxZIndex = $derived(
|
||||
manifest.items.length > 0
|
||||
? Math.max(...manifest.items.map((i) => i.zIndex))
|
||||
: 0,
|
||||
);
|
||||
|
||||
async function save() {
|
||||
@@ -92,7 +95,7 @@ function createState() {
|
||||
for (const [id, asset] of Object.entries(assets)) {
|
||||
storedAssets[id] = {
|
||||
dataUrl: await blobToDataUrl(asset.blob),
|
||||
filename: asset.filename
|
||||
filename: asset.filename,
|
||||
};
|
||||
}
|
||||
const stored: StoredState = { manifest, assets: storedAssets };
|
||||
@@ -168,11 +171,11 @@ function createState() {
|
||||
}
|
||||
|
||||
function select(id: string | null) {
|
||||
selectedIds = new Set(id ? [id] : []);
|
||||
selectedIds = new SvelteSet(id ? [id] : []);
|
||||
}
|
||||
|
||||
function toggleSelection(id: string) {
|
||||
const newSet = new Set(selectedIds);
|
||||
const newSet = new SvelteSet(selectedIds);
|
||||
if (newSet.has(id)) {
|
||||
newSet.delete(id);
|
||||
} else {
|
||||
@@ -182,7 +185,7 @@ function createState() {
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedIds = new Set();
|
||||
selectedIds = new SvelteSet();
|
||||
}
|
||||
|
||||
function edit(id: string | null) {
|
||||
@@ -219,19 +222,27 @@ function createState() {
|
||||
function copySelected() {
|
||||
if (selectedIds.size === 0) return;
|
||||
const items = manifest.items.filter((i) => selectedIds.has(i.id));
|
||||
clipboard = items.map(({ id, ...rest }) => rest);
|
||||
clipboard = items.map(({ id: _id, ...rest }) => rest);
|
||||
}
|
||||
|
||||
function pasteItems(x: number, y: number): string[] {
|
||||
if (clipboard.length === 0) return [];
|
||||
const newIds: string[] = [];
|
||||
const centerX = clipboard.reduce((sum, i) => sum + i.x, 0) / clipboard.length;
|
||||
const centerY = clipboard.reduce((sum, i) => sum + i.y, 0) / clipboard.length;
|
||||
const centerX =
|
||||
clipboard.reduce((sum, i) => sum + i.x, 0) / clipboard.length;
|
||||
const centerY =
|
||||
clipboard.reduce((sum, i) => sum + i.y, 0) / clipboard.length;
|
||||
for (const item of clipboard) {
|
||||
const id = crypto.randomUUID();
|
||||
const offsetX = item.x - centerX;
|
||||
const offsetY = item.y - centerY;
|
||||
addItem({ ...item, id, x: x + offsetX, y: y + offsetY, zIndex: maxZIndex + 1 });
|
||||
addItem({
|
||||
...item,
|
||||
id,
|
||||
x: x + offsetX,
|
||||
y: y + offsetY,
|
||||
zIndex: maxZIndex + 1,
|
||||
});
|
||||
newIds.push(id);
|
||||
}
|
||||
return newIds;
|
||||
@@ -316,7 +327,11 @@ function createState() {
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
function animateViewport(targetX: number, targetY: number, targetZoom: number) {
|
||||
function animateViewport(
|
||||
targetX: number,
|
||||
targetY: number,
|
||||
targetZoom: number,
|
||||
) {
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
|
||||
const startX = viewport.x;
|
||||
@@ -358,11 +373,11 @@ function createState() {
|
||||
items: [],
|
||||
sharedCss: DEFAULT_SHARED_CSS,
|
||||
appCss: DEFAULT_APP_CSS,
|
||||
flags: {}
|
||||
flags: {},
|
||||
};
|
||||
assets = {};
|
||||
viewport = { x: 0, y: 0, zoom: 1 };
|
||||
selectedIds = new Set();
|
||||
selectedIds = new SvelteSet();
|
||||
editingId = null;
|
||||
editingGlobal = false;
|
||||
focusedId = null;
|
||||
@@ -374,7 +389,7 @@ function createState() {
|
||||
manifest = newManifest;
|
||||
assets = newAssets;
|
||||
viewport = { x: 0, y: 0, zoom: 1 };
|
||||
selectedIds = new Set();
|
||||
selectedIds = new SvelteSet();
|
||||
editingId = null;
|
||||
editingGlobal = false;
|
||||
focusedId = null;
|
||||
@@ -438,7 +453,7 @@ function createState() {
|
||||
clearFlag,
|
||||
gotoFlag,
|
||||
reset,
|
||||
load
|
||||
load,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
105
src/lib/theme.ts
105
src/lib/theme.ts
@@ -3,7 +3,9 @@ import { syntaxHighlighting, HighlightStyle } from '@codemirror/language';
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
function getVar(name: string, fallback: string): string {
|
||||
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||||
const value = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(name)
|
||||
.trim();
|
||||
return value || fallback;
|
||||
}
|
||||
|
||||
@@ -27,104 +29,145 @@ export function createTheme() {
|
||||
'&': {
|
||||
backgroundColor: surface,
|
||||
color: '#abb2bf',
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
overflow: 'auto',
|
||||
fontFamily: "'Departure Mono', monospace"
|
||||
fontFamily: "'Departure Mono', monospace",
|
||||
},
|
||||
'.cm-content': {
|
||||
caretColor: accent
|
||||
caretColor: accent,
|
||||
},
|
||||
'.cm-cursor, .cm-dropCursor': {
|
||||
borderLeftColor: accent
|
||||
borderLeftColor: accent,
|
||||
},
|
||||
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
|
||||
{
|
||||
backgroundColor: '#3E4451'
|
||||
backgroundColor: '#3E4451',
|
||||
},
|
||||
'.cm-panels': {
|
||||
backgroundColor: surface,
|
||||
color: '#abb2bf'
|
||||
color: '#abb2bf',
|
||||
},
|
||||
'.cm-panels.cm-panels-top': {
|
||||
borderBottom: `1px solid ${border}`
|
||||
borderBottom: `1px solid ${border}`,
|
||||
},
|
||||
'.cm-panels.cm-panels-bottom': {
|
||||
borderTop: `1px solid ${border}`
|
||||
borderTop: `1px solid ${border}`,
|
||||
},
|
||||
'.cm-searchMatch': {
|
||||
backgroundColor: '#72a1ff59',
|
||||
outline: `1px solid ${border}`
|
||||
outline: `1px solid ${border}`,
|
||||
},
|
||||
'.cm-searchMatch.cm-searchMatch-selected': {
|
||||
backgroundColor: '#6199ff2f'
|
||||
backgroundColor: '#6199ff2f',
|
||||
},
|
||||
'.cm-activeLine': {
|
||||
backgroundColor: '#2c313c50'
|
||||
backgroundColor: '#2c313c50',
|
||||
},
|
||||
'.cm-selectionMatch': {
|
||||
backgroundColor: '#aafe661a'
|
||||
backgroundColor: '#aafe661a',
|
||||
},
|
||||
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||
backgroundColor: '#bad0f847'
|
||||
backgroundColor: '#bad0f847',
|
||||
},
|
||||
'.cm-gutters': {
|
||||
backgroundColor: surface,
|
||||
color: textDim,
|
||||
border: 'none'
|
||||
border: 'none',
|
||||
},
|
||||
'.cm-activeLineGutter': {
|
||||
backgroundColor: '#2c313c50'
|
||||
backgroundColor: '#2c313c50',
|
||||
},
|
||||
'.cm-foldPlaceholder': {
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
color: textDim
|
||||
color: textDim,
|
||||
},
|
||||
'.cm-tooltip': {
|
||||
border: 'none',
|
||||
backgroundColor: surface
|
||||
backgroundColor: surface,
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:before': {
|
||||
borderTopColor: 'transparent',
|
||||
borderBottomColor: 'transparent'
|
||||
borderBottomColor: 'transparent',
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:after': {
|
||||
borderTopColor: surface,
|
||||
borderBottomColor: surface
|
||||
borderBottomColor: surface,
|
||||
},
|
||||
'.cm-tooltip-autocomplete': {
|
||||
'& > ul > li[aria-selected]': {
|
||||
backgroundColor: accent,
|
||||
color: text
|
||||
}
|
||||
}
|
||||
color: text,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dark: true }
|
||||
{ dark: true },
|
||||
);
|
||||
|
||||
const highlighting = HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: keyword },
|
||||
{ tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], color: variable },
|
||||
{
|
||||
tag: [
|
||||
tags.name,
|
||||
tags.deleted,
|
||||
tags.character,
|
||||
tags.propertyName,
|
||||
tags.macroName,
|
||||
],
|
||||
color: variable,
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: func },
|
||||
{ tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: number },
|
||||
{
|
||||
tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)],
|
||||
color: number,
|
||||
},
|
||||
{ tag: [tags.definition(tags.name), tags.separator], color: '#abb2bf' },
|
||||
{ tag: [tags.typeName, tags.className, tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace], color: number },
|
||||
{ tag: [tags.operator, tags.operatorKeyword, tags.url, tags.escape, tags.regexp, tags.link, tags.special(tags.string)], color: operator },
|
||||
{
|
||||
tag: [
|
||||
tags.typeName,
|
||||
tags.className,
|
||||
tags.number,
|
||||
tags.changed,
|
||||
tags.annotation,
|
||||
tags.modifier,
|
||||
tags.self,
|
||||
tags.namespace,
|
||||
],
|
||||
color: number,
|
||||
},
|
||||
{
|
||||
tag: [
|
||||
tags.operator,
|
||||
tags.operatorKeyword,
|
||||
tags.url,
|
||||
tags.escape,
|
||||
tags.regexp,
|
||||
tags.link,
|
||||
tags.special(tags.string),
|
||||
],
|
||||
color: operator,
|
||||
},
|
||||
{ tag: [tags.meta, tags.comment], color: comment, fontStyle: 'italic' },
|
||||
{ tag: tags.strong, fontWeight: 'bold' },
|
||||
{ tag: tags.emphasis, fontStyle: 'italic' },
|
||||
{ tag: tags.strikethrough, textDecoration: 'line-through' },
|
||||
{ tag: tags.link, color: comment, textDecoration: 'underline' },
|
||||
{ tag: tags.heading, fontWeight: 'bold', color: variable },
|
||||
{ tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: number },
|
||||
{ tag: [tags.processingInstruction, tags.string, tags.inserted], color: string },
|
||||
{
|
||||
tag: [tags.atom, tags.bool, tags.special(tags.variableName)],
|
||||
color: number,
|
||||
},
|
||||
{
|
||||
tag: [tags.processingInstruction, tags.string, tags.inserted],
|
||||
color: string,
|
||||
},
|
||||
{ tag: tags.invalid, color: '#ff0000' },
|
||||
{ tag: tags.tagName, color: variable },
|
||||
{ tag: tags.attributeName, color: number },
|
||||
{ tag: tags.attributeValue, color: string },
|
||||
{ tag: tags.propertyName, color: func }
|
||||
{ tag: tags.propertyName, color: func },
|
||||
]);
|
||||
|
||||
return [theme, syntaxHighlighting(highlighting)];
|
||||
|
||||
12
src/main.ts
12
src/main.ts
@@ -1,9 +1,9 @@
|
||||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
import { mount } from 'svelte';
|
||||
import './app.css';
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!,
|
||||
})
|
||||
target: document.getElementById('app')!,
|
||||
});
|
||||
|
||||
export default app
|
||||
export default app;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import("@sveltejs/vite-plugin-svelte").SvelteConfig} */
|
||||
export default {
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
}
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
};
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"types": ["svelte", "vite/client"],
|
||||
"noEmit": true,
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"moduleDetection": "force"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"types": ["svelte", "vite/client"],
|
||||
"noEmit": true,
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"moduleDetection": "force"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import { defineConfig } from 'vite';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
})
|
||||
plugins: [svelte()],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user