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']
}
-});
+})