diff --git a/.editorconfig b/.editorconfig index d57b269a4..f5f23d8d4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,5 @@ indent_size = 2 [*.yml] indent_size = 2 - [*.properties] insert_final_newline = false diff --git a/.github/workflows/make-doc.sh b/.github/workflows/make-doc.sh index f7446f84c..f86ef60f0 100755 --- a/.github/workflows/make-doc.sh +++ b/.github/workflows/make-doc.sh @@ -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" diff --git a/.github/workflows/make-doc.yml b/.github/workflows/make-doc.yml index 3abf5d265..18ce815a5 100644 --- a/.github/workflows/make-doc.yml +++ b/.github/workflows/make-doc.yml @@ -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 diff --git a/.gitignore b/.gitignore index 84f17aee8..808032e6d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/build.gradle b/build.gradle index 33a414ab9..5c36a17fd 100644 --- a/build.gradle +++ b/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 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 { diff --git a/doc/styles.css b/doc/styles.css deleted file mode 100644 index a21b9ac34..000000000 --- a/doc/styles.css +++ /dev/null @@ -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; -} diff --git a/illuaminate.sexp b/illuaminate.sexp index 558028975..80a481a64 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -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__))) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..b12022dac --- /dev/null +++ b/package-lock.json @@ -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 + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..682f93de0 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 000000000..1e0342fea --- /dev/null +++ b/rollup.config.js @@ -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`; + }, + } + ], +}; diff --git a/src/web/copy-cat.d.ts b/src/web/copy-cat.d.ts new file mode 100644 index 000000000..b4a85d267 --- /dev/null +++ b/src/web/copy-cat.d.ts @@ -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 { + public render(props: MainProps, state: unknown): ComponentChild; +} + +export { Computer }; diff --git a/src/web/index.tsx b/src/web/index.tsx new file mode 100644 index 000000000..0bf476a86 --- /dev/null +++ b/src/web/index.tsx @@ -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 }) => + + +type WindowProps = {}; + +type WindowState = { + visible: boolean, + + example: string, + exampleIdx: number, +} + +type Touch = { clientX: number, clientY: number }; + +class Window extends Component { + 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(, 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 ?
+
+
+ +
+
+ +
+
:
; + } + + 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(, document.body, root); diff --git a/src/web/mount/.settings b/src/web/mount/.settings new file mode 100644 index 000000000..5b9849575 --- /dev/null +++ b/src/web/mount/.settings @@ -0,0 +1,3 @@ +{ + [ "motd.enable" ] = false, +} diff --git a/src/web/mount/expr_template.lua b/src/web/mount/expr_template.lua new file mode 100644 index 000000000..37faf4180 --- /dev/null +++ b/src/web/mount/expr_template.lua @@ -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))) diff --git a/src/web/mount/startup.lua b/src/web/mount/startup.lua new file mode 100644 index 000000000..91bbf535f --- /dev/null +++ b/src/web/mount/startup.lua @@ -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) diff --git a/src/web/styles.css b/src/web/styles.css new file mode 100644 index 000000000..e60e670ee --- /dev/null +++ b/src/web/styles.css @@ -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; } +} diff --git a/tools/check-lines.py b/tools/check-lines.py index 3659924ae..47ff15fc9 100644 --- a/tools/check-lines.py +++ b/tools/check-lines.py @@ -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 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..b6dc1da3f --- /dev/null +++ b/tsconfig.json @@ -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", + ] +}