Setup editor, remove tauri for now
15
README.md
|
@ -2,13 +2,16 @@
|
|||
|
||||
|
||||
# planned for v1
|
||||
- [ ] sprite manager
|
||||
- [ ] level manager
|
||||
- [ ] monaco editor
|
||||
- [ ] **export project for sprig**
|
||||
- [ ] ability to play project
|
||||
- [ ] monaco code editor
|
||||
- [ ] export project for sprig
|
||||
- [ ] docs site
|
||||
- [ ] typescript support (esbuild prob)
|
||||
|
||||
|
||||
# planned for v2
|
||||
- [ ] typescript support (esbuild prob)
|
||||
- [ ] build project as a standalone `.html` file (maybe vite?)
|
||||
- [ ] sprite manager
|
||||
- [ ] level manager
|
||||
- [ ] build project as a standalone `.html` file
|
||||
- [ ] run on device
|
||||
- [ ] downloadable version
|
BIN
bun.lockb
14
package.json
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "sprig-rs",
|
||||
"name": "sprigsy",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -17,6 +17,7 @@
|
|||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@tailwindcss/typography": "^0.5.14",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-walk": "^3.0.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
|
@ -24,12 +25,13 @@
|
|||
"svelte-check": "^3.6.0",
|
||||
"tailwindcss": "^3.4.9",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"vite": "^5.0.3",
|
||||
"@babel/parser": "^7.25.3",
|
||||
"@sveltejs/adapter-static": "^3.0.4",
|
||||
"constrained-editor-plugin": "^1.3.0",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"sprig": "^1.1.3",
|
||||
"sveltekit-adapter-deno": "^0.12.1"
|
||||
}
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
3
src-tauri/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
3689
src-tauri/Cargo.lock
generated
|
@ -1,26 +0,0 @@
|
|||
[package]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
default-run = "app"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.5.3", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.7.0", features = [] }
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
||||
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
||||
# DO NOT REMOVE!!
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
|
@ -1,3 +0,0 @@
|
|||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 9 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 49 KiB |
|
@ -1,8 +0,0 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
{
|
||||
"build": {
|
||||
"beforeBuildCommand": "bun run build",
|
||||
"beforeDevCommand": "bun run dev",
|
||||
"devPath": "http://localhost:5173",
|
||||
"distDir": "../buid"
|
||||
},
|
||||
"package": {
|
||||
"productName": "sprigsy",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.tauri.dev",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"title": "Sprigsy Editor",
|
||||
"width": 800
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
body, html {
|
||||
|
||||
}
|
191
src/lib/Editor.svelte
Normal file
|
@ -0,0 +1,191 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||
import {} from 'sprig/web';
|
||||
import { constrainedEditor } from 'constrained-editor-plugin';
|
||||
import { parse } from '@babel/parser';
|
||||
|
||||
let editorContainer: HTMLDivElement;
|
||||
let editor: import('monaco-editor').editor.IStandaloneCodeEditor;
|
||||
// const monaco = await import ('monaco-editor')
|
||||
// Initial code inside the function
|
||||
function debounce<A, T = void>(func: (this: T, ...args: A[]) => any, timeout = 300) {
|
||||
let timer: number;
|
||||
return function (this: T, ...args: A[]) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
}, timeout);
|
||||
};
|
||||
}
|
||||
let code = `console.log('Hello sprig!');`;
|
||||
onMount(async () => {
|
||||
globalThis.process = {};
|
||||
globalThis.process.env = {};
|
||||
const { recursive } = await import('babel-walk');
|
||||
await addGlobalTypes();
|
||||
setupEditor();
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_: any, label: string) {
|
||||
return new tsWorker();
|
||||
}
|
||||
};
|
||||
|
||||
// Function to add global types
|
||||
async function addGlobalTypes() {
|
||||
const sprigTypeDef = (await import('./editorcontext/sprig.d.ts?raw')).default;
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
sprigTypeDef,
|
||||
'context/sprig.d.ts'
|
||||
);
|
||||
const globalTypeDef = (await import('./editorcontext/global.d.ts?raw')).default;
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
globalTypeDef,
|
||||
'context/global.d.ts'
|
||||
);
|
||||
|
||||
// IMPLEMENT ONCE v2 IS DONE!
|
||||
// const sprigsyTypeDef = (await import('./editorcontext/sprigsy.d.ts?raw')).default;
|
||||
// monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
// sprigsyTypeDef,
|
||||
// 'context/sprigsy.d.ts'
|
||||
// );
|
||||
}
|
||||
const disallowedIdentifiers = ['window', 'globalThis'];
|
||||
type ASTState = {
|
||||
disallowedWarnings: { identifier: string; loc: [number, number, number, number] }[];
|
||||
};
|
||||
const ASTWalker = recursive<ASTState>({
|
||||
Identifier(node, state, c) {
|
||||
console;
|
||||
if (disallowedIdentifiers.includes(node.name)) {
|
||||
state.disallowedWarnings.push({
|
||||
identifier: node.name,
|
||||
loc: [
|
||||
node.loc?.start?.line!,
|
||||
node.loc?.start?.column!,
|
||||
node.loc?.end?.line!,
|
||||
node.loc?.end?.column!
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const checkBabel = debounce(() => {
|
||||
console.log('checking for uhh');
|
||||
const ast = parse(editor.getValue(), {
|
||||
plugins: ['typescript']
|
||||
});
|
||||
const state: ASTState = { disallowedWarnings: [] };
|
||||
ASTWalker(ast, state);
|
||||
console.log(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
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// Function to set up the editor
|
||||
function setupEditor() {
|
||||
editor = monaco.editor.create(editorContainer, {
|
||||
value: `function game(api: sprig.FullSprigAPI): void {
|
||||
${code}
|
||||
}`,
|
||||
language: 'typescript',
|
||||
automaticLayout: true,
|
||||
scrollBeyondLastLine: false,
|
||||
theme: 'vs-dark'
|
||||
});
|
||||
|
||||
const model = editor.getModel()!;
|
||||
// Mark function declaration and closing brace as read-only
|
||||
// editor.createDecorationsCollection(
|
||||
// [
|
||||
// {
|
||||
// range: new monaco.Range(1, 1, 1, 28),
|
||||
// options: {
|
||||
// className: 'read-only',
|
||||
// isWholeLine: true
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// range: new monaco.Range(
|
||||
// model.getLineCount(),
|
||||
// 1,
|
||||
// model.getLineCount(),
|
||||
// model.getLineMaxColumn(model.getLineCount())
|
||||
// ),
|
||||
// options: {
|
||||
// className: 'read-only',
|
||||
// isWholeLine: true
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// );
|
||||
|
||||
// Disable editing in read-only regions
|
||||
// editor.onDidChangeModelContent((event) => {
|
||||
// event.changes.forEach((change) => {
|
||||
// const { range } = change;
|
||||
|
||||
// if (
|
||||
// (range.startLineNumber === 1 && range.endLineNumber === 1) ||
|
||||
// (range.startLineNumber === model.getLineCount() &&
|
||||
// range.endLineNumber === model.getLineCount())
|
||||
// ) {
|
||||
// editor.executeEdits('revert-read-only', [
|
||||
// {
|
||||
// range: new monaco.Range(
|
||||
// range.startLineNumber,
|
||||
// range.startColumn,
|
||||
// range.endLineNumber,
|
||||
// range.endColumn
|
||||
// ),
|
||||
// text: '',
|
||||
// forceMoveMarkers: true
|
||||
// }
|
||||
// ]);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
const constrainedInstance = constrainedEditor(monaco);
|
||||
constrainedInstance.initializeIn(editor);
|
||||
constrainedInstance.addRestrictionsTo(model, [
|
||||
{
|
||||
range: [2, 1, 2, code.length + 2], // Range of Function definition
|
||||
allowMultiline: true,
|
||||
label: 'funcDefinition'
|
||||
}
|
||||
]);
|
||||
editor.onDidChangeModelContent((event) => {
|
||||
console.log(event.changes);
|
||||
console.log(model.getValueInEditableRanges());
|
||||
checkBabel();
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
if (editor) {
|
||||
editor.dispose();
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={editorContainer} class="editor-container"></div>
|
||||
|
||||
<style>
|
||||
.editor-container {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
10
src/lib/editorcontext/global.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
import "./sprig"
|
||||
|
||||
declare global {
|
||||
var setTimeout: sprig.FullSprigAPI['setTimeout'];
|
||||
var setInterval: sprig.FullSprigAPI['setInterval'];
|
||||
var clearTimeout: sprig.FullSprigAPI['clearTimeout'];
|
||||
var clearInterval: sprig.FullSprigAPI['clearInterval'];
|
||||
|
||||
var tones: typeof sprig['tones']
|
||||
}
|
75
src/lib/editorcontext/sprig.d.ts
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
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<string, string[]>;
|
||||
background: string | null;
|
||||
}
|
||||
export interface PlayTuneRes {
|
||||
end(): void;
|
||||
isPlaying(): boolean;
|
||||
}
|
||||
// export declare const tones: Record<string, number>;
|
||||
// export declare const instruments: readonly ["sine", "triangle", "square", "sawtooth"];
|
||||
export type InstrumentType = typeof instruments[number];
|
||||
// export declare const instrumentKey: Record<string, InstrumentType>;
|
||||
// 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<string, string[]>): 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;
|
||||
}
|
||||
}
|
11
src/lib/editorcontext/sprigsy.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* API for sprigsy
|
||||
* this is provided because your project has sprigsy v2 enabeled
|
||||
*/
|
||||
declare namespace sprigsy {
|
||||
/**
|
||||
* Retrieve a sprite key for the sprite name
|
||||
* @param name Sprite name
|
||||
*/
|
||||
function spriteKeyFor(name: string): string
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
export const prerender = true;
|
||||
export const ssr = false
|
|
@ -1,2 +1,8 @@
|
|||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||
<script>
|
||||
import Editor from "$lib/Editor.svelte";
|
||||
|
||||
</script>
|
||||
<div class="">
|
||||
<Editor />
|
||||
|
||||
</div>
|
||||
|
|