diff --git a/.prettierrc b/.prettierrc index 3f7802c..ada5e7c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,7 @@ { "useTabs": true, "singleQuote": true, + "semi": false, "trailingComma": "none", "printWidth": 100, "plugins": ["prettier-plugin-svelte"], diff --git a/bun.lockb b/bun.lockb index a89b87f..43584e3 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..bea1efe --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install.scopes] +"@jsr" = "https://npm.jsr.io" diff --git a/package.json b/package.json index c770d44..10141d1 100644 --- a/package.json +++ b/package.json @@ -15,20 +15,25 @@ }, "devDependencies": { "@babel/parser": "^7.26.5", + "@babel/traverse": "^7.26.5", + "@codemirror/lang-javascript": "^6.2.2", + "@std/async": "npm:@jsr/std__async", "@sveltejs/adapter-static": "^3.0.6", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@tailwindcss/postcss": "^4.0.0-beta.9", - "babel-walk": "^3.0.1", - "constrained-editor-plugin": "^1.3.0", + "@types/babel__traverse": "^7.20.6", + "@typescript/vfs": "^1.6.0", + "@valtown/codemirror-ts": "^2.3.1", + "codemirror": "^6.0.1", "daisyui": "^5.0.0-beta.1", "esbuild-wasm": "^0.24.2", - "monaco-editor": "^0.52.2", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.6", "sprig": "^1.1.3", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "svelte-codemirror-editor": "^1.4.1", "tailwindcss": "^4.0.0-beta.9", "typescript": "^5.0.0", "vite": "^5.4.11", diff --git a/postcss.config.js b/postcss.config.js index c5c819c..1e7a895 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,5 +1,5 @@ export default { plugins: { - '@tailwindcss/postcss': {}, + '@tailwindcss/postcss': {} } -}; +} diff --git a/src/app.css b/src/app.css index b932777..f406f04 100644 --- a/src/app.css +++ b/src/app.css @@ -11,11 +11,11 @@ color utility to any element that depends on these defaults. */ @layer base { - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentColor); - } -} \ No newline at end of file + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } +} diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..4e661d5 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -10,4 +10,4 @@ declare global { } } -export {}; +export {} diff --git a/src/lib/CodeEditor.svelte b/src/lib/CodeEditor.svelte index 791e1b3..d3a4d91 100644 --- a/src/lib/CodeEditor.svelte +++ b/src/lib/CodeEditor.svelte @@ -1,153 +1,39 @@ - -
+ + editor = e.detail} on:change={(e) => { + console.log(e) +}} bind:value {extensions} class="editor-container" /> - diff --git a/src/lib/CodeEditorOld.svelte b/src/lib/CodeEditorOld.svelte new file mode 100644 index 0000000..0f243ea --- /dev/null +++ b/src/lib/CodeEditorOld.svelte @@ -0,0 +1,147 @@ + + +
+ + diff --git a/src/lib/build/esbuild.ts b/src/lib/build/esbuild.ts index b083a69..0be93b5 100644 --- a/src/lib/build/esbuild.ts +++ b/src/lib/build/esbuild.ts @@ -1,23 +1,23 @@ -import { transform, initialize } from 'esbuild-wasm'; +import { transform, initialize } from 'esbuild-wasm' await initialize({ wasmURL: (await import('esbuild-wasm/esbuild.wasm?url')).default -}); +}) export async function buildProject(code: string, vanilla: boolean) { - let template = ''; + let template = '' if (vanilla) { template += `async function game(api) { ${code} } console.log("Made with Sprigsy") game({ addSprite, addText, afterInput, bitmap, clearInterval, clearText, clearTile, clearTimeout, color, getAll, getFirst, getGrid, getTile, height, map, onInput, playTune, setBackground, setInterval, setLegend, setMap, setPushables, setSolids, setTimeout, tilesWith, tune, width }) -`; +` } else { - throw new Error('not implemented'); + throw new Error('not implemented') } return await transform(template, { - loader: 'ts', - }); + loader: 'ts' + }) } diff --git a/src/lib/components/Tab.svelte b/src/lib/components/Tab.svelte index 477b6dc..c4a2585 100644 --- a/src/lib/components/Tab.svelte +++ b/src/lib/components/Tab.svelte @@ -1,5 +1,5 @@
diff --git a/src/lib/editorcontext/sprig.d.ts b/src/lib/editorcontext/sprig.d.ts deleted file mode 100644 index ba51f4b..0000000 --- a/src/lib/editorcontext/sprig.d.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * normal sprig api - * this is provided because your project type is - */ -declare namespace sprig { - export declare const VALID_INPUTS: readonly ['w', 's', 'a', 'd', 'i', 'j', 'k', 'l']; - export type InputKey = (typeof VALID_INPUTS)[number]; - export interface AddTextOptions { - x?: number; - y?: number; - color?: string; - } - export declare class SpriteType { - type: string; - x: number; - y: number; - readonly dx: number; - readonly dy: number; - remove(): void; - } - export type Rgba = [number, number, number, number]; - export interface TextElement { - x: number; - y: number; - color: Rgba; - content: string; - } - export interface GameState { - legend: [string, string][]; - texts: TextElement[]; - dimensions: { - width: number; - height: number; - }; - sprites: SpriteType[]; - solids: string[]; - pushable: Record; - background: string | null; - } - export interface PlayTuneRes { - end(): void; - isPlaying(): boolean; - } - // export declare const tones: Record; - // export declare const instruments: readonly ["sine", "triangle", "square", "sawtooth"]; - export type InstrumentType = (typeof instruments)[number]; - // export declare const instrumentKey: Record; - // export declare const reverseInstrumentKey: Record<"sine" | "triangle" | "square" | "sawtooth", string>; - export type Tune = [number, ...(InstrumentType | number | string)[]][]; - export interface FullSprigAPI { - map(template: TemplateStringsArray, ...params: string[]): string; - bitmap(template: TemplateStringsArray, ...params: string[]): string; - color(template: TemplateStringsArray, ...params: string[]): string; - tune(template: TemplateStringsArray, ...params: string[]): string; - setMap(string: string): void; - addText(str: string, opts?: AddTextOptions): void; - clearText(): void; - addSprite(x: number, y: number, type: string): void; - getGrid(): SpriteType[][]; - getTile(x: number, y: number): SpriteType[]; - tilesWith(...matchingTypes: string[]): SpriteType[][]; - clearTile(x: number, y: number): void; - setSolids(types: string[]): void; - setPushables(map: Record): void; - setBackground(type: string): void; - getFirst(type: string): SpriteType | undefined; - getAll(type?: string): SpriteType[]; - width(): number; - height(): number; - setLegend(...bitmaps: [string, string][]): void; - onInput(key: InputKey, fn: () => void): void; - afterInput(fn: () => void): void; - playTune(text: string, n?: number): PlayTuneRes; - setTimeout(fn: TimerHandler, ms: number): number; - setInterval(fn: TimerHandler, ms: number): number; - clearTimeout(id: number): void; - clearInterval(id: number): void; - } -} diff --git a/src/lib/provider/Provider.ts b/src/lib/provider/Provider.ts index cd3eb64..6e3f077 100644 --- a/src/lib/provider/Provider.ts +++ b/src/lib/provider/Provider.ts @@ -1,30 +1,26 @@ -import { buildProject } from "$lib/build/esbuild" +import { buildProject } from '$lib/build/esbuild' enum Capability { - BuildToVite, - ProjectDB + BuildToVite, + ProjectDB } export default abstract class Provider { - constructor( - - ) { - - } + constructor() {} - abstract capabilities(): Capability[] + abstract capabilities(): Capability[] - /** - * Builds HTML project - */ - async buildHTML(input: string): Promise { - throw new TypeError("BuildToVite capability is not available") - } + /** + * Builds HTML project + */ + async buildHTML(input: string): Promise { + throw new TypeError('BuildToVite capability is not available') + } - /** - * Build JS - */ - async buildJS(input: string): Promise { - return (await buildProject(input, true)).code - } -} \ No newline at end of file + /** + * Build JS + */ + async buildJS(input: string): Promise { + return (await buildProject(input, true)).code + } +} diff --git a/src/lib/text-editor/Tooltip.svelte b/src/lib/text-editor/Tooltip.svelte new file mode 100644 index 0000000..14a57c7 --- /dev/null +++ b/src/lib/text-editor/Tooltip.svelte @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/src/lib/text-editor/babel.ts b/src/lib/text-editor/babel.ts new file mode 100644 index 0000000..0220cb7 --- /dev/null +++ b/src/lib/text-editor/babel.ts @@ -0,0 +1,71 @@ +import { debounce } from '@std/async' +import { parse } from '@babel/parser' + +let traverse: typeof import("@babel/traverse")['default'] + +export async function setupBabel() { + traverse = (await import("@babel/traverse")).default +} + +const disallowedIdentifiers = ['window', 'globalThis'] +type ASTState = { + disallowedWarnings: { identifier: string; loc: [number, number, number, number] }[] + errors: { reason: string; loc: [number, number, number, number] }[] +} + +export default debounce((code: string) => { + let ast + try { + ast = parse(code, { + plugins: ['typescript'] + }) + } catch { + return + } + const state: ASTState = { disallowedWarnings: [], errors: [] } + traverse(ast, { + MemberExpression: { + enter: (path, state) => { + if (path.node.optional) { + state.errors.push({ + reason: + 'optional chaining (?.) will work in the editor but it will not work on the sprig console', + loc: [ + path.node.loc?.start.line!, + path.node.loc?.start.column!, + path.node.loc?.end.line!, + path.node.loc?.end.column! + ] + }) + } + } + }, + Identifier: { + enter: (path, state) => { + if (disallowedIdentifiers.includes(path.node.name)) { + state.disallowedWarnings.push({ + identifier: path.node.name, + loc: [ + path.node.loc?.start?.line!, + path.node.loc?.start?.column!, + path.node.loc?.end?.line!, + path.node.loc?.end?.column! + ] + }) + } + } + }, + }, undefined, state) + state.disallowedWarnings.forEach((v) => { + monaco.editor.setModelMarkers(editor.getModel()!, editor.getId(), [ + { + message: `'${v.identifier}' is not allowed.`, + severity: monaco.MarkerSeverity.Error, + startLineNumber: v.loc[0], + startColumn: v.loc[1] + 1, + endLineNumber: v.loc[2], + endColumn: v.loc[3] + 1 + } + ]) + }) +}, 300) diff --git a/src/lib/text-editor/context/sprig.d.ts b/src/lib/text-editor/context/sprig.d.ts new file mode 100644 index 0000000..b16fe56 --- /dev/null +++ b/src/lib/text-editor/context/sprig.d.ts @@ -0,0 +1,84 @@ +/** + * basic sprig API + * this is provided because your project type is base + */ +declare global { + declare namespace sprig { + export declare const VALID_INPUTS: readonly ['w', 's', 'a', 'd', 'i', 'j', 'k', 'l'] + export type InputKey = (typeof VALID_INPUTS)[number] + export interface AddTextOptions { + x?: number + y?: number + color?: string + } + export declare class SpriteType { + type: string + x: number + y: number + readonly dx: number + readonly dy: number + remove(): void + } + export type Rgba = [number, number, number, number] + export interface TextElement { + x: number + y: number + color: Rgba + content: string + } + export interface GameState { + legend: [string, string][] + texts: TextElement[] + dimensions: { + width: number + height: number + } + sprites: SpriteType[] + solids: string[] + pushable: Record + background: string | null + } + export interface PlayTuneRes { + end(): void + isPlaying(): boolean + } + // export declare const tones: Record; + // export declare const instruments: readonly ["sine", "triangle", "square", "sawtooth"]; + export type InstrumentType = (typeof instruments)[number] + // export declare const instrumentKey: Record; + // export declare const reverseInstrumentKey: Record<"sine" | "triangle" | "square" | "sawtooth", string>; + export type Tune = [number, ...(InstrumentType | number | string)[]][] + export interface FullSprigAPI { + map(template: TemplateStringsArray, ...params: string[]): string + bitmap(template: TemplateStringsArray, ...params: string[]): string + color(template: TemplateStringsArray, ...params: string[]): string + tune(template: TemplateStringsArray, ...params: string[]): string + setMap(string: string): void + addText(str: string, opts?: AddTextOptions): void + clearText(): void + addSprite(x: number, y: number, type: string): void + getGrid(): SpriteType[][] + getTile(x: number, y: number): SpriteType[] + tilesWith(...matchingTypes: string[]): SpriteType[][] + clearTile(x: number, y: number): void + setSolids(types: string[]): void + setPushables(map: Record): void + setBackground(type: string): void + getFirst(type: string): SpriteType | undefined + getAll(type?: string): SpriteType[] + width(): number + height(): number + setLegend(...bitmaps: [string, string][]): void + onInput(key: InputKey, fn: () => void): void + afterInput(fn: () => void): void + playTune(text: string, n?: number): PlayTuneRes + setTimeout(fn: TimerHandler, ms: number): number + setInterval(fn: TimerHandler, ms: number): number + clearTimeout(id: number): void + clearInterval(id: number): void + } + } + +} + +export {}; \ No newline at end of file diff --git a/src/lib/editorcontext/sprigsy.d.ts b/src/lib/text-editor/context/sprigsy.d.ts similarity index 79% rename from src/lib/editorcontext/sprigsy.d.ts rename to src/lib/text-editor/context/sprigsy.d.ts index a3050e1..6ffda06 100644 --- a/src/lib/editorcontext/sprigsy.d.ts +++ b/src/lib/text-editor/context/sprigsy.d.ts @@ -1,4 +1,5 @@ /** + * THIS SHOULD NOT BE HERE * API for sprigsy * this is provided because your project has sprigsy v2 enabeled */ @@ -8,6 +9,6 @@ declare namespace sprigsy { * Retrieve a sprite key for the sprite name * @param name Sprite name */ - spriteKeyFor(name: string): string; + spriteKeyFor(name: string): string } } diff --git a/src/lib/text-editor/extensions/babel.ts b/src/lib/text-editor/extensions/babel.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/text-editor/extensions/typescript.ts b/src/lib/text-editor/extensions/typescript.ts new file mode 100644 index 0000000..02977ef --- /dev/null +++ b/src/lib/text-editor/extensions/typescript.ts @@ -0,0 +1,62 @@ +import { javascript } from '@codemirror/lang-javascript' +import { autocompletion } from '@codemirror/autocomplete' +import Tooltip from '../Tooltip.svelte' +import { mount, unmount } from 'svelte' +import { + createDefaultMapFromCDN, + createSystem, + createVirtualTypeScriptEnvironment +} from '@typescript/vfs' +import ts from 'typescript' +import { tsFacet, tsLinter, tsHover, tsAutocomplete, tsSync } from '@valtown/codemirror-ts' + +import sprigDeclarations from '$lib/text-editor/context/sprig.d.ts?raw' + +const fsMap = await createDefaultMapFromCDN({ target: ts.ScriptTarget.ES2022 }, '5.7.3', true, ts) +fsMap.set('lib.sprig.d.ts', sprigDeclarations) +const system = createSystem(fsMap) +const compilerOpts = {} +const env = createVirtualTypeScriptEnvironment(system, ['lib.sprig.d.ts'], ts, compilerOpts) + +console.log(fsMap.entries().toArray()) +const path = 'index.ts' + +export const typescriptExtensions = [ + javascript({ + jsx: false, + typescript: true + }), + tsFacet.of({ env, path }), + tsSync(), + tsLinter(), + autocompletion({ + override: [tsAutocomplete()], + + }), + tsHover({ + renderTooltip: (info) => { + console.log("rendering tooltip") + let tooltip: Tooltip; + const div = document.createElement('div') + return { + dom: div, + mount() { + tooltip = mount(Tooltip, { + target: div, + intro: true, + props: { + info + } + }) + }, + destroy() { + unmount(tooltip, { outro: true }) + } + } + } + }) +] + +export { + env +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 3b7359a..5f8f30a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,6 +1,6 @@ {@render children()} diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 0a06f03..9c1daa0 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -1,2 +1,3 @@ -export const csr = true; -export const prerender = true; \ No newline at end of file +export const csr = true +export const prerender = true +export const ssr = false diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index eae1fe2..7d9d521 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,8 +1,11 @@
@@ -56,3 +59,5 @@ This will have the game screen {/if}
+ + \ No newline at end of file diff --git a/svelte.config.js b/svelte.config.js index 4fbc867..89d00e0 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,18 +1,17 @@ -import adapter from '@sveltejs/adapter-static'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import adapter from '@sveltejs/adapter-static' +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://svelte.dev/docs/kit/integrations // for more information about preprocessors preprocess: vitePreprocess(), - kit: { // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. // If your environment is not supported, or you settled on a specific environment, switch out the adapter. // See https://svelte.dev/docs/kit/adapters for more information about adapters. adapter: adapter() } -}; +} -export default config; +export default config diff --git a/vite.config.ts b/vite.config.ts index 8a4c5fd..0848c3c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,9 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite' +import { defineConfig } from 'vite' export default defineConfig({ plugins: [sveltekit()], build: { target: ['es2022'] } -}); +})