mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-11 18:50:30 +00:00
Runnable examples (#576)
Provides a basic interface for running examples on tweaked.cc. This is probably janky as anything, but it works on my machine. This is the culmination of 18 months of me building far too much infrastructure (copy-cat, illuaminate), so that's nice I guess. I should probably get out more.
This commit is contained in:
parent
c8aeddedd4
commit
a4c9e89370
@ -17,6 +17,5 @@ indent_size = 2
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
[*.properties]
|
||||
insert_final_newline = false
|
||||
|
2
.github/workflows/make-doc.sh
vendored
2
.github/workflows/make-doc.sh
vendored
@ -12,5 +12,5 @@ chmod 600 "$HOME/.ssh/key"
|
||||
|
||||
# And upload
|
||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||
"$GITHUB_WORKSPACE/doc/out/" \
|
||||
"$GITHUB_WORKSPACE/build/docs/lua/" \
|
||||
"$SSH_USER@$SSH_HOST:/var/www/tweaked.cc/$DEST"
|
||||
|
18
.github/workflows/make-doc.yml
vendored
18
.github/workflows/make-doc.yml
vendored
@ -30,18 +30,20 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||
|
||||
- name: Generate Java documentation stubs
|
||||
run: ./gradlew luaJavadoc --no-daemon
|
||||
|
||||
- name: Build documentation
|
||||
- name: Setup illuaminate
|
||||
run: |
|
||||
test -d bin || mkdir bin
|
||||
test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
||||
chmod +x bin/illuaminate
|
||||
bin/illuaminate doc-gen
|
||||
|
||||
- name: Setup node
|
||||
run: npm ci
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||
|
||||
- name: Generate documentation
|
||||
run: ./gradlew docWebsite --no-daemon
|
||||
|
||||
- name: Upload documentation
|
||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -4,7 +4,7 @@
|
||||
/build
|
||||
/out
|
||||
/doc/out/
|
||||
/doc/javadoc/
|
||||
/node_modules
|
||||
|
||||
# Runtime directories
|
||||
/run
|
||||
@ -18,10 +18,12 @@
|
||||
.gradle
|
||||
*.DS_Store
|
||||
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
/.vscode
|
||||
bin/
|
||||
*.launch
|
||||
|
||||
/src/generated/resources/.cache
|
||||
/src/web/mount/*.d.ts
|
||||
|
52
build.gradle
52
build.gradle
@ -136,7 +136,7 @@ task luaJavadoc(type: Javadoc) {
|
||||
group "documentation"
|
||||
|
||||
source = sourceSets.main.allJava
|
||||
destinationDir = file("doc/javadoc")
|
||||
destinationDir = file("${project.docsDir}/luaJavadoc")
|
||||
classpath = sourceSets.main.compileClasspath
|
||||
|
||||
options.docletpath = configurations.cctJavadoc.files as List
|
||||
@ -306,6 +306,56 @@ task compressJson(dependsOn: jar) {
|
||||
|
||||
assemble.dependsOn compressJson
|
||||
|
||||
// Web tasks
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
List<String> mkCommand(String command) {
|
||||
return Os.isFamily(Os.FAMILY_WINDOWS) ? ["cmd", "/c", command] : ["sh", "-c", command]
|
||||
}
|
||||
|
||||
task rollup(type: Exec) {
|
||||
group = "build"
|
||||
description = "Bundles JS into rollup"
|
||||
|
||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||
inputs.file("rollup.config.js").withPropertyName("Rollup config")
|
||||
outputs.file("$buildDir/rollup/index.js").withPropertyName("output")
|
||||
|
||||
commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js')
|
||||
}
|
||||
|
||||
task minifyWeb(type: Exec, dependsOn: rollup) {
|
||||
group = "build"
|
||||
description = "Bundles JS into rollup"
|
||||
|
||||
inputs.file("$buildDir/rollup/index.js").withPropertyName("sources")
|
||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
||||
outputs.file("$buildDir/rollup/index.min.js").withPropertyName("output")
|
||||
|
||||
commandLine mkCommand('"node_modules/.bin/terser"' + " -o $buildDir/rollup/index.min.js $buildDir/rollup/index.js")
|
||||
}
|
||||
|
||||
task illuaminateDocs(type: Exec, dependsOn: [minifyWeb, luaJavadoc]) {
|
||||
group = "build"
|
||||
description = "Bundles JS into rollup"
|
||||
|
||||
inputs.files(fileTree("doc")).withPropertyName("sources")
|
||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||
inputs.file("$buildDir/rollup/index.min.js").withPropertyName("scripts")
|
||||
inputs.file("src/web/styles.css").withPropertyName("styles")
|
||||
outputs.dir("$buildDir/docs/lua")
|
||||
|
||||
commandLine mkCommand('"bin/illuaminate" doc-gen')
|
||||
}
|
||||
|
||||
task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
|
||||
from 'doc/logo.png'
|
||||
into "${project.docsDir}/lua"
|
||||
}
|
||||
|
||||
// Check tasks
|
||||
|
||||
test {
|
||||
|
@ -1,14 +0,0 @@
|
||||
/* Pretty tables, mostly inherited from table.definition-list */
|
||||
table.pretty-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.pretty-table td, table.pretty-table th {
|
||||
border: 1px solid #cccccc;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
table.pretty-table th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
@ -2,18 +2,20 @@
|
||||
|
||||
(sources
|
||||
/doc/stub/
|
||||
/doc/javadoc/
|
||||
/build/docs/luaJavadoc/
|
||||
/src/main/resources/*/computercraft/lua/bios.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/
|
||||
/src/test/resources/test-rom)
|
||||
/src/test/resources/test-rom
|
||||
/src/web/mount)
|
||||
|
||||
|
||||
(doc
|
||||
(title "CC: Tweaked")
|
||||
(destination doc/out)
|
||||
(destination build/docs/lua)
|
||||
(logo src/main/resources/pack.png)
|
||||
(index doc/index.md)
|
||||
(styles doc/styles.css)
|
||||
(styles src/web/styles.css)
|
||||
(scripts build/rollup/index.js)
|
||||
(source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||
|
||||
(module-kinds
|
||||
@ -21,7 +23,7 @@
|
||||
|
||||
(library-path
|
||||
/doc/stub/
|
||||
/doc/javadoc/
|
||||
/build/docs/luaJavadoc/
|
||||
|
||||
/src/main/resources/*/computercraft/lua/rom/apis
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/command
|
||||
@ -72,7 +74,7 @@
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
;; Silence some variable warnings in documentation stubs.
|
||||
(at (/doc/stub/ /doc/javadoc/)
|
||||
(at (/doc/stub/ /build/docs/luaJavadoc/)
|
||||
(linters -var:unused-global)
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
@ -84,11 +86,11 @@
|
||||
/doc/stub/turtle.lua
|
||||
/doc/stub/global.lua
|
||||
; Java generated APIs
|
||||
/doc/javadoc/turtle.lua
|
||||
/build/docs/luaJavadoc/turtle.lua
|
||||
; Peripherals
|
||||
/doc/javadoc/drive.lua
|
||||
/doc/javadoc/speaker.lua
|
||||
/doc/javadoc/printer.lua
|
||||
/build/docs/luaJavadoc/drive.lua
|
||||
/build/docs/luaJavadoc/speaker.lua
|
||||
/build/docs/luaJavadoc/printer.lua
|
||||
; Lua APIs
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/io.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua)
|
||||
@ -116,3 +118,5 @@
|
||||
(globals
|
||||
:max sleep write
|
||||
cct_test describe expect howlci fail it pending stub)))
|
||||
|
||||
(at /src/web/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||
|
172
package-lock.json
generated
Normal file
172
package-lock.json
generated
Normal file
@ -0,0 +1,172 @@
|
||||
{
|
||||
"name": "tweaked.cc",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@rollup/plugin-typescript": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-6.1.0.tgz",
|
||||
"integrity": "sha512-hJxaiE6WyNOsK+fZpbFh9CUijZYqPQuAOWO5khaGTUkM8DYNNyA2TDlgamecE+qLOG1G1+CwbWMAx3rbqpp6xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
"resolve": "^1.17.0"
|
||||
}
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "0.0.39",
|
||||
"estree-walker": "^1.0.1",
|
||||
"picomatch": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"@types/estree": {
|
||||
"version": "0.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
||||
"dev": true
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"estree-walker": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz",
|
||||
"integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.5.5",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.5.tgz",
|
||||
"integrity": "sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg=="
|
||||
},
|
||||
"requirejs": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
|
||||
"integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
|
||||
"integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.0.0",
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.33.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz",
|
||||
"integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.1.2"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.3.8.tgz",
|
||||
"integrity": "sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.19"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
|
||||
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
18
package.json
Normal file
18
package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "tweaked.cc",
|
||||
"version": "1.0.0",
|
||||
"description": "Website additions for tweaked.cc",
|
||||
"author": "SquidDev",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"preact": "^10.5.5",
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^6.1.0",
|
||||
"requirejs": "^2.3.6",
|
||||
"rollup": "^2.33.1",
|
||||
"terser": "^5.3.8",
|
||||
"typescript": "^4.0.5"
|
||||
}
|
||||
}
|
49
rollup.config.js
Normal file
49
rollup.config.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { readFileSync, promises as fs } from "fs";
|
||||
import path from "path";
|
||||
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
|
||||
const input = "src/web";
|
||||
const requirejs = readFileSync("node_modules/requirejs/require.js");
|
||||
|
||||
export default {
|
||||
input: [`${input}/index.tsx`],
|
||||
output: {
|
||||
file: "build/rollup/index.js",
|
||||
// We bundle requirejs (and config) into the header. It's rather gross
|
||||
// but also works reasonably well.
|
||||
banner: `${requirejs}\nrequire.config({ paths: { copycat: "https://copy-cat.squiddev.cc" } });`,
|
||||
format: "amd",
|
||||
preferConst: true,
|
||||
amd: {
|
||||
define: "require",
|
||||
}
|
||||
},
|
||||
context: "window",
|
||||
external: ["copycat/embed"],
|
||||
|
||||
plugins: [
|
||||
typescript(),
|
||||
|
||||
{
|
||||
name: "cc-tweaked",
|
||||
async options(options) {
|
||||
// Generate .d.ts files for all /mount files. This is the worst way to do it,
|
||||
// but we need to run before the TS pass.
|
||||
const template = "declare const contents : string;\nexport default contents;\n";
|
||||
const files = await fs.readdir(`${input}/mount`);
|
||||
|
||||
await Promise.all(files
|
||||
.filter(x => path.extname(x) !== ".ts")
|
||||
.map(file => fs.writeFile(`${input}/mount/${file}.d.ts`, template))
|
||||
);
|
||||
return options;
|
||||
},
|
||||
async transform(code, file) {
|
||||
// Allow loading files in /mount.
|
||||
if (path.extname(file) != ".lua" && path.basename(file) != ".settings") return null;
|
||||
return `export default ${JSON.stringify(code)};\n`;
|
||||
},
|
||||
}
|
||||
],
|
||||
};
|
21
src/web/copy-cat.d.ts
vendored
Normal file
21
src/web/copy-cat.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { h, Component, render, ComponentChild } from "preact";
|
||||
|
||||
export { h, Component, render };
|
||||
|
||||
export type ComputerAccess = unknown;
|
||||
|
||||
export type MainProps = {
|
||||
hdFont?: boolean | string,
|
||||
persistId?: number,
|
||||
files?: { [filename: string]: string | ArrayBuffer },
|
||||
label?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
resolve?: (computer: ComputerAccess) => void,
|
||||
}
|
||||
|
||||
declare class Computer extends Component<MainProps, unknown> {
|
||||
public render(props: MainProps, state: unknown): ComponentChild;
|
||||
}
|
||||
|
||||
export { Computer };
|
155
src/web/index.tsx
Normal file
155
src/web/index.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
import { render, h, Component, Computer } from "copycat/embed";
|
||||
import type { ComponentChild } from "preact";
|
||||
|
||||
import settingsFile from "./mount/.settings";
|
||||
import startupFile from "./mount/startup.lua";
|
||||
import exprTemplate from "./mount/expr_template.lua";
|
||||
|
||||
const defaultFiles: { [filename: string]: string } = {
|
||||
".settings": settingsFile,
|
||||
"startup.lua": startupFile,
|
||||
};
|
||||
|
||||
const clamp = (value: number, min: number, max: number): number => {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
const Click = (options: { run: () => void }) =>
|
||||
<button type="button" class="example-run" onClick={options.run}>Run ᐅ</button>
|
||||
|
||||
type WindowProps = {};
|
||||
|
||||
type WindowState = {
|
||||
visible: boolean,
|
||||
|
||||
example: string,
|
||||
exampleIdx: number,
|
||||
}
|
||||
|
||||
type Touch = { clientX: number, clientY: number };
|
||||
|
||||
class Window extends Component<WindowProps, WindowState> {
|
||||
private positioned: boolean = false;
|
||||
private left: number = 0;
|
||||
private top: number = 0;
|
||||
private dragging?: { downX: number, downY: number, initialX: number, initialY: number };
|
||||
|
||||
constructor(props: WindowProps, context: unknown) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
visible: false,
|
||||
example: "",
|
||||
exampleIdx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const elements = document.querySelectorAll("pre[data-lua-kind]");
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i] as HTMLElement;
|
||||
|
||||
let example = element.innerText;
|
||||
if (element.getAttribute("data-lua-kind") == "expr") {
|
||||
example = exprTemplate.replace("__expr__", example);
|
||||
}
|
||||
render(<Click run={this.runExample(example)} />, element);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(_: WindowProps, { visible }: WindowState) {
|
||||
if (!visible && this.state.visible) this.setPosition(this.left, this.top);
|
||||
}
|
||||
|
||||
public render(_: WindowProps, { visible, example, exampleIdx }: WindowState): ComponentChild {
|
||||
return visible ? <div class="example-window" style={`transform: translate(${this.left}px, ${this.top}px);`}>
|
||||
<div class="titlebar">
|
||||
<div class="titlebar-drag" onMouseDown={this.onMouseDown} onTouchStart={this.onTouchDown} />
|
||||
<button type="button" class="titlebar-close" onClick={this.close}>{"\u2715"}</button>
|
||||
</div>
|
||||
<div class="computer-container">
|
||||
<Computer key={exampleIdx} files={{
|
||||
"example.lua": example, ...defaultFiles
|
||||
}} />
|
||||
</div>
|
||||
</div> : <div class="example-window example-window-hidden" />;
|
||||
}
|
||||
|
||||
private runExample(example: string): () => void {
|
||||
return () => {
|
||||
if (!this.positioned) {
|
||||
this.positioned = true;
|
||||
this.left = 20;
|
||||
this.top = 20;
|
||||
}
|
||||
|
||||
this.setState(({ exampleIdx }: WindowState) => ({
|
||||
visible: true,
|
||||
example: example,
|
||||
exampleIdx: exampleIdx + 1,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private readonly close = () => this.setState({ visible: false });
|
||||
|
||||
// All the dragging code is terrible. However, I've had massive performance
|
||||
// issues doing it other ways, so this'll have to do.
|
||||
private onDown(e: Event, touch: Touch) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this.dragging = {
|
||||
initialX: this.left, initialY: this.top,
|
||||
downX: touch.clientX, downY: touch.clientY
|
||||
};
|
||||
|
||||
window.addEventListener("mousemove", this.onMouseDrag, true);
|
||||
window.addEventListener("touchmove", this.onTouchDrag, true);
|
||||
window.addEventListener("mouseup", this.onUp, true);
|
||||
window.addEventListener("touchend", this.onUp, true);
|
||||
}
|
||||
private readonly onMouseDown = (e: MouseEvent) => this.onDown(e, e);
|
||||
private readonly onTouchDown = (e: TouchEvent) => this.onDown(e, e.touches[0]);
|
||||
|
||||
private onDrag(e: Event, touch: Touch) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const dragging = this.dragging;
|
||||
if (!dragging) return;
|
||||
|
||||
this.setPosition(
|
||||
dragging.initialX + (touch.clientX - dragging.downX),
|
||||
dragging.initialY + (touch.clientY - dragging.downY),
|
||||
);
|
||||
};
|
||||
private readonly onMouseDrag = (e: MouseEvent) => this.onDrag(e, e);
|
||||
private readonly onTouchDrag = (e: TouchEvent) => this.onDrag(e, e.touches[0]);
|
||||
|
||||
private readonly onUp = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
|
||||
this.dragging = undefined;
|
||||
|
||||
window.removeEventListener("mousemove", this.onMouseDrag, true);
|
||||
window.removeEventListener("touchmove", this.onTouchDrag, true);
|
||||
window.removeEventListener("mouseup", this.onUp, true);
|
||||
window.removeEventListener("touchend", this.onUp, true);
|
||||
}
|
||||
|
||||
private readonly setPosition = (left: number, top: number): void => {
|
||||
const root = this.base as HTMLElement;
|
||||
|
||||
left = this.left = clamp(left, 0, window.innerWidth - root.offsetWidth);
|
||||
top = this.top = clamp(top, 0, window.innerHeight - root.offsetHeight);
|
||||
root.style.transform = `translate(${left}px, ${top}px)`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const root = document.createElement("div");
|
||||
document.body.appendChild(root);
|
||||
render(<Window />, document.body, root);
|
3
src/web/mount/.settings
Normal file
3
src/web/mount/.settings
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
[ "motd.enable" ] = false,
|
||||
}
|
14
src/web/mount/expr_template.lua
Normal file
14
src/web/mount/expr_template.lua
Normal file
@ -0,0 +1,14 @@
|
||||
local result = table.pack(__expr__
|
||||
)
|
||||
|
||||
if result.n == 0 then return end
|
||||
|
||||
local pp = require "cc.pretty"
|
||||
|
||||
local line = {}
|
||||
for i = 1, result.n do
|
||||
if i > 1 then line[#line + 1] = pp.text(", ") end
|
||||
line[#line + 1] = pp.pretty(result[i])
|
||||
end
|
||||
|
||||
pp.print(pp.concat(table.unpack(line)))
|
5
src/web/mount/startup.lua
Normal file
5
src/web/mount/startup.lua
Normal file
@ -0,0 +1,5 @@
|
||||
-- Make the startup file invisible, then run the file. We could use
|
||||
-- shell.run, but this ensures the program is in shell history, etc...
|
||||
fs.delete("startup.lua")
|
||||
os.queueEvent("paste", "example.lua")
|
||||
os.queueEvent("key", keys.enter, false)
|
85
src/web/styles.css
Normal file
85
src/web/styles.css
Normal file
@ -0,0 +1,85 @@
|
||||
/* Pretty tables, mostly inherited from table.definition-list */
|
||||
table.pretty-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.pretty-table td, table.pretty-table th {
|
||||
border: 1px solid #cccccc;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
table.pretty-table th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.highlight.highlight-lua {
|
||||
position: relative;
|
||||
background: #eee;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.example-run {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: #058e05;
|
||||
color: #fff;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.example-window {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
top: 0px;
|
||||
top: 0px;;
|
||||
}
|
||||
|
||||
/* Behold, the most cursed CSS! copy-cat's resizing algorithm is a weird, in
|
||||
that it basically does "wrapper height - 40px" to determine the effective
|
||||
size. But the footer is actually 1em+6px high, so we need to do very weird
|
||||
things.
|
||||
|
||||
Yes, it should probably be fixed on the copy-cat side.
|
||||
*/
|
||||
.computer-container {
|
||||
width: 620px;
|
||||
height: calc(350px + 40px);
|
||||
margin-top: calc((1em + 6px - 40px) / 2);
|
||||
}
|
||||
|
||||
.example-window-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
display: flex;
|
||||
background: #dede6c;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.titlebar-drag {
|
||||
flex-grow: 1;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.titlebar-close {
|
||||
background: none;
|
||||
padding: 0px 5px;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
margin: 0px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.titlebar-close:hover { background: #cc4c4c; }
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.computer-container {
|
||||
width: 314px;
|
||||
height: calc(179px + 40px);
|
||||
}
|
||||
|
||||
.titlebar { height: 20px; }
|
||||
.titlebar-close { font-size: 7px; }
|
||||
}
|
@ -18,7 +18,7 @@ for path in pathlib.Path("src").glob("**/*"):
|
||||
if line.strip() == "":
|
||||
print("%s has empty first line" % path)
|
||||
|
||||
if len(line) >= 2 and line[-2] == "\r" and line[-1] == "\n" and not has_line:
|
||||
if len(line) >= 2 and line[-2] == "\r" and line[-1] == "\n" and not has_dos:
|
||||
print("%s has contains '\\r\\n' on line %d" % (path, i + 1))
|
||||
problems = has_dos = True
|
||||
|
||||
|
34
tsconfig.json
Normal file
34
tsconfig.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esNext",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
],
|
||||
"newLine": "LF",
|
||||
"baseUrl": ".",
|
||||
// Additional compile options
|
||||
"noEmitOnError": true,
|
||||
"preserveWatchOutput": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "h",
|
||||
// Strict Type-Checking Options
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"paths": {
|
||||
"copycat/embed": [
|
||||
"src/web/copy-cat.d.ts"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/web",
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user