├── .gitignore ├── .gitmodules ├── CHANGES.md ├── LICENSE ├── LICENSES-FONTS.txt ├── README.md ├── about.html ├── apphooks.js ├── cli.sh ├── docicon-glulx.ico ├── docicon-hugo.ico ├── docicon-json.ico ├── docicon-tads.ico ├── docicon-zcode.ico ├── el-glkote.css ├── emglken ├── LICENSE ├── README.md ├── build.py ├── gi_load.min.js ├── git-core.wasm ├── git.js ├── glulxe-core.wasm ├── glulxe.js ├── hugo-core.wasm ├── hugo.js ├── scare-core.wasm ├── scare.js ├── tads-core.wasm ├── tads.js └── versions.json ├── emglkenplay.html ├── font ├── GenBkBasB.ttf ├── GenBkBasBI.ttf ├── GenBkBasI.ttf ├── GenBkBasR.ttf ├── LibreBaskerville-Bold.ttf ├── LibreBaskerville-Italic.ttf ├── LibreBaskerville-Regular.ttf ├── Lora-Bold.ttf ├── Lora-BoldItalic.ttf ├── Lora-Italic.ttf ├── Lora-Regular.ttf ├── SourceCodePro-Bold.ttf ├── SourceCodePro-BoldIt.ttf ├── SourceCodePro-It.ttf ├── SourceCodePro-Regular.ttf ├── SourceSansPro-Bold.ttf ├── SourceSansPro-BoldIt.ttf ├── SourceSansPro-It.ttf └── SourceSansPro-Regular.ttf ├── fonts.css ├── fonts.js ├── formats.js ├── icon-128.png ├── icon-tray.ico ├── if-card.html ├── if-card.js ├── ifvms ├── LICENSE ├── README.md ├── build.py ├── gi_load.min.js ├── package.json ├── zvm.css ├── zvm.min.js ├── zvm_dispatch.js └── zvm_dispatch.min.js ├── inkjs ├── ink-130.min.js ├── ink-146.min.js ├── ink-160.min.js ├── ink.min.js └── package.json ├── inkplay.html ├── inkplay.js ├── main.js ├── makedist.py ├── package.json ├── play.css ├── play.html ├── prefs.html ├── prefs.js ├── resources ├── Add-Info.plist ├── appicon-dmg.icns ├── appicon-dmg.svg ├── appicon-mac.icns ├── appicon-win.ico ├── appicon.svg ├── create-dmg-args.txt ├── icon-blorb.icns ├── icon-blorb.svg ├── icon-gblorb.icns ├── icon-gblorb.svg ├── icon-glkdata.icns ├── icon-glkdata.svg ├── icon-glksave.icns ├── icon-glksave.svg ├── icon-glulx.icns ├── icon-glulx.svg ├── icon-hugo.icns ├── icon-hugo.svg ├── icon-json.icns ├── icon-json.svg ├── icon-tads-s.png ├── icon-tads.icns ├── icon-tads.png ├── icon-tads.svg ├── icon-zblorb.icns ├── icon-zblorb.svg ├── icon-zcode.icns ├── icon-zcode.svg ├── mac-app.entitlements ├── pack-dmg-background.png ├── pack-dmg-background.svg ├── pack-dmg-spec.json └── wininstaller.nsi ├── samplegame ├── Advent.ulx ├── about.html ├── package.json ├── play.html └── resources │ └── pack-dmg-spec.json ├── searchbar.js ├── tools └── file ├── tragen.js ├── transcript.html ├── transcript.js ├── traread.js ├── trashow.html ├── trashow.js └── zplay.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | dist 4 | tempapp 5 | pack-dmg-spec-tmp.json 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "quixe"] 2 | path = quixe 3 | url = https://github.com/erkyrath/quixe.git 4 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2 | ## 1.5.4 (Jun ###, 2025) 3 | 4 | - Glk spec 0.7.6: the glk_image_draw_scaled_ext() call. 5 | - Bumped Electron to 34.5.7. 6 | 7 | ## 1.5.3 (Feb 22, 2025) 8 | 9 | - Added a "Retain Transcripts" preference: "Forever", "Last N transcripts", "For N days". Defaults to "Forever", which was the previous behavior. 10 | - If you open a game and its transcript at the same time, game updates will appear in the transcript window. 11 | - If you open the same game in two windows, they will have separate transcripts. Also, the second one will not try to use the autosave slot. (Two windows fighting over the same autosave is bad.) 12 | - The build process now produces a "Lectrote-X.Y.Z-WinInstall.exe" installer product (using makensis). This installs the win32-x64 version. 13 | - Bumped Electron to 33.4.1. 14 | 15 | ## 1.5.2 (Dec 2, 2024) 16 | 17 | - Fixed the Find menu options in game windows. They now also work in transcript windows. 18 | 19 | ## 1.5.1 (Oct 12, 2024) 20 | 21 | - Added a "Show File Location" menu option (shows the current game file or transcript file in the OS file browser). 22 | - Show the text "Image N" or the provided alt text where an image was displayed in the original game. (Story windows only). 23 | - If the timestamp option is on for a transcript window, saving as text will include timestamps in the text file. 24 | - Bumped Electron to 31.7.0. 25 | 26 | ## 1.5.0 (Sep 29, 2024) 27 | 28 | - Universal transcript mode! A transcript is saved for every game you play. Select the "Browse Transcripts" menu option to see a list. Transcripts are displayed as rich text (the same Glk stylesheet as the original game); the "Save as Text" option allows you to save a plain text version. 29 | - Updated emglken to 0.6.0 (fixed TADS save/restore bugs, monospace support, increased undo limit; consistent RNG behavior for Glulx interpreters; fixed a Unicode bug). 30 | - Updated inkjs to 2.3.0. 31 | - Minor Quixe optimizations. 32 | 33 | ## 1.4.6 (Jun 3, 2024) 34 | 35 | - The margin preference can now go up to 35%. 36 | - Updated inkjs to 2.2.4. 37 | - Bumped Electron to 29.4.2. 38 | - Mac version now requires MacOS 10.15 "Catalina" or later. 39 | 40 | ## 1.4.5 (Sep 19, 2023) 41 | 42 | - Fix a packaging bug that prevented Adrift from working. 43 | 44 | ## 1.4.4 (Sep 2, 2023) 45 | 46 | - Updates for emglken and glkote. (Experimental Adrift 4 support; various TADS bugs.) 47 | - Updated inkjs to 2.2.2. 48 | - Bumped Electron to 24.8.2. 49 | 50 | ## 1.4.3 (Jul 21, 2022) 51 | 52 | - Fix a path bug in emglken. 53 | 54 | ## 1.4.2 (Jul 16, 2022) 55 | 56 | - Accept ".sav" as a valid file suffix when loading save files (in addition to ".glksave"). 57 | - Updated Quixe to 2.2.1 (matching Glulx VM 3.1.3, now with double-precision math). 58 | - Update inkjs to 2.1.0 (matching ink v1.0). 59 | - Update emglken to 0.4.0. (Improved display of TADS status windows; most recent versions of bocfel, hugo, glulxe, and git VMs.) 60 | - Bumped Electron to 18.3.5. 61 | 62 | ## 1.4.1 (Jan 3, 2022) 63 | 64 | - Added a tray icon on Windows. This allows you to quit the app if it is running with no windows open. (Right-click on the tray icon for a Quit menu option.) 65 | - Added Windows ARM and Linux ARM to the release platform list. 66 | - Bumped Electron to 14.2.3. 67 | - Fixed some bugs building bound apps. 68 | - Linting and code cleanup down in the GlkOte library. 69 | 70 | ## 1.4.0 (Mar 20, 2021) 71 | 72 | - TADS save/load works now! Although it's somewhat slow. 73 | - Scrollback buffer is now 800 lines or paragraphs. 74 | - Bumped Electron to 11.3.0. 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Lectrote is copyright (c) 2016-2023, Andrew Plotkin [MIT license] 2 | Electron is copyright (c) 2013-2023 GitHub Inc. [MIT license] 3 | Quixe is copyright (c) 2010-2023, Andrew Plotkin [MIT license] 4 | inkjs is copyright (c) 2017 Yannick Lohse [MIT license] 5 | ifvms.js is copyright (c) 2011-2023 Dannii Willis and other contributors [MIT license] 6 | emglken is copyright (c) 2012-2023, Andrew Plotkin, Dannii Willis [MIT license] 7 | Git (in emglken) is copyright (c) 2003 Iain Merrick [MIT license] 8 | Glulxe (in emglken) is copyright (c) 1999-2023, Andrew Plotkin [MIT license] 9 | Hugo (in emglken) is copyright (c) 2011 by Kent Tessman [BSD license] 10 | TADS (in emglken) is copyright (c) 1991-2012 by Michael J. Roberts [dual-licensed GPL/TADS license] 11 | RemGlk (in emglken) is copyright (c) 2012-2023, Andrew Plotkin [MIT license] 12 | Scare (in emglken) is copyright (c) 2003-2008, Simon Baldwin and Mark J. Tilford [GPL] 13 | 14 | ------------------- 15 | 16 | The MIT License 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | 36 | ------------------- 37 | 38 | BSD 2-clause "Simplified" License (for Hugo component of emglken) 39 | 40 | Hugo is copyright (c) 2011 by Kent Tessman 41 | All rights reserved. 42 | 43 | Redistribution and use in source and binary forms, with or without 44 | modification, are permitted provided that the following conditions are 45 | met: 46 | 47 | - Redistributions of source code must retain the above copyright 48 | notice, this list of conditions and the following disclaimer. 49 | 50 | - Redistributions in binary form must reproduce the above copyright 51 | notice, this list of conditions and the following disclaimer in the 52 | documentation and/or other materials provided with the distribution. 53 | 54 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 55 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 56 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 57 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 58 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 59 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 60 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 61 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 62 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 63 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 64 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 65 | 66 | ------------------- 67 | 68 | TADS 2 FREEWARE SOURCE CODE LICENSE 69 | 70 | The TADS 2 source code is Copyright 1991, 2003 by Michael J. Roberts. 71 | 72 | The author hereby grants you permission to use, copy, and 73 | distribute this software, if you agree to the following conditions: 74 | 75 | 1. You must include this license and the copyright notice with 76 | all copies. 77 | 2. You may not require or collect a fee for copies of this 78 | software, or any part of this software, that you give to 79 | other people. 80 | 3. You may not include this software with any other software 81 | for which a fee is collected. 82 | 4. You may not modify this software except as permitted below 83 | (see "derivative works"), and each copy you make and 84 | distribute must be a full and complete copy of the software 85 | you originally received. 86 | 5. Anyone to whom you give a copy of this software receives 87 | all of the same permissions that you did under this license 88 | and is subject to all of the same restrictions. 89 | 6. You are not allowed to create derivative works, which are 90 | works that contain or are based on all or part of this work, 91 | except under the conditions described below. 92 | 7. Any derivative works are subject to this same license. 93 | 94 | 95 | Derivative Works 96 | ---------------- 97 | 98 | This source code is distributed for the specific purpose of 99 | facilitating the creation of versions of TADS on various computers and 100 | operating systems. All other derivative works are prohibited without 101 | the written permission of the author. Please contact the author if 102 | you have any questions about this or if you'd like permission to 103 | create a derived work. 104 | 105 | If you port TADS to a new platform, the author does grant permission 106 | for you to distribute your ported version - I encourage it, in fact. 107 | We ask that you provide your contact information in any distribution 108 | package you create, so that users of your version will know how to 109 | contact you if they have any questions relating specifically to your 110 | version. 111 | 112 | ------------------- 113 | 114 | TADS 3 FREEWARE SOURCE CODE LICENSE 115 | 116 | The TADS 3 source code is Copyright 1998, 2012 by Michael J. Roberts. 117 | 118 | The author hereby grants you permission to use, copy, and distribute 119 | this software, if you agree to the following conditions: 120 | 121 | 1. You must include this license and the copyright notice with 122 | all copies. 123 | 2. You may not require or collect a fee for copies of this 124 | software, or any part of this software, that you give to 125 | other people. 126 | 3. You may not include this software with any other software 127 | for which a fee is collected. 128 | 4. You may not modify this software except as permitted below 129 | (see "derivative works"), and each copy you make and 130 | distribute must be a full and complete copy of the software 131 | you originally received. 132 | 5. Anyone to whom you give a copy of this software receives 133 | all of the same permissions that you did under this license 134 | and is subject to all of the same restrictions. 135 | 6. You are not allowed to create derivative works, which are 136 | works that contain or are based on all or part of this work, 137 | except under the conditions described below. 138 | 7. Any derivative works are subject to this same license. 139 | 140 | 141 | Derivative Works 142 | ---------------- 143 | 144 | This source code is distributed for the specific purpose of porting 145 | TADS, so that you can run the software on any system of your choosing. 146 | All other derivative works are prohibited without the written 147 | permission of the author. I want to avoid the creation of variations 148 | on the system, because it leads to confusion on the part of users if 149 | there are multiple incompatible flavors floating around. However, if 150 | you have a specific idea in mind, I'd be happy to at least consider 151 | it. Please contact the author if you have any questions about this or 152 | if you would like permission to create a derived work. 153 | 154 | If you port TADS to a new platform, the author does grant permission 155 | for you to distribute your ported version - I encourage it, in fact. 156 | I ask that you include your contact information in any distribution 157 | package you create, so that users of your version will know how to 158 | contact you if they have any questions relating specifically to your 159 | version. 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lectrote 2 | Lectrote logo: purple compass 3 | 4 | ### The IF interpreter in an [Electron][] shell 5 | 6 | - Version 1.5.4 7 | - Created by Andrew Plotkin 8 | - [Download the latest Lectrote app][releases] 9 | 10 | [Electron]: https://www.electronjs.org 11 | [Node]: https://nodejs.org/en 12 | [releases]: https://github.com/erkyrath/lectrote/releases 13 | 14 | Lectrote packages up IF interpreters with the Chromium browser as a [Mac/Win/Linux app][releases]. 15 | 16 | When launched, it prompts you to select a game file to play. You can play several games at the same time in separate windows. Your position is always autosaved; when you launch a game, your last play session will automatically be resumed. 17 | 18 | Lectrote currently supports: 19 | 20 | - [Glulx][] games (`.ulx` or `.gblorb`), as produced by [Inform 7][i7]. 21 | - [Z-code][] games (`.z3/.z4/.z5/.z8` or `.zblorb`), as produced by [Inform 7][i7] or earlier versions of Inform. 22 | - [Hugo][] games (`.hex`). 23 | - [TADS2 and TADS3][tads] games (`.gam`, `.t3`). 24 | - [Adrift 4][adrift4] games (`.taf`). 25 | - [Ink][] compiled game files (`.json`), as produced by the [Ink][] scripting language. 26 | 27 | [i7]: http://inform7.com/ 28 | [Glulx]: https://eblong.com/zarf/glulx/ 29 | [Hugo]: http://www.generalcoffee.com/hugo/gethugo.html 30 | [Ink]: https://www.inklestudios.com/ink 31 | [tads]: https://tads.org/ 32 | [adrift4]: https://www.adrift.co/ 33 | [Z-code]: https://inform-fiction.org/zmachine/standards/z1point1 34 | 35 | You can also use this package to construct a "bound game" -- an app which plays a single built-in game. This is a package containing Chromium, the interpreter, your game file, and perhaps some additional configuration. You can distribute this as a standalone game application; it's bulky but it lets people play your game. 36 | 37 | Linux note: Depending on your Linux configuration and how you install this package, you may have to add the `--no-sandbox` option when launching Lectrote. 38 | 39 | ## Glulx (Inform 7) support 40 | 41 | Because this relies on the [Quixe][] interpreter, sound is not supported. It's also not as fast as a native interpreter. 42 | 43 | [Quixe]: http://eblong.com/zarf/glulx/quixe/ 44 | 45 | ## Z-code support 46 | 47 | Lectrote uses the [ZVM][] interpreter for Z-machine support. (V3/4/5 and V8 only.) 48 | 49 | [ZVM]: https://github.com/curiousdannii/ifvms.js 50 | 51 | ## Hugo support 52 | 53 | The Hugo engine does not currently support autosave. 54 | 55 | ## TADS support 56 | 57 | The TADS 2/3 engine does not currently support autosave. 58 | 59 | ## Adrift 4 support 60 | 61 | The Adrift 4 engine does not currently support autosave. 62 | 63 | ## Ink support 64 | 65 | This relies on the [inkjs][] interpreter. It is a deliberately non-fancy presentation -- no attempt to slow-print the output or hide the choice list. 66 | 67 | [inkjs]: https://github.com/y-lohse/inkjs 68 | 69 | # License information 70 | 71 | - Lectrote is copyright (c) 2016-2023, Andrew Plotkin ([MIT license][licensefile]) 72 | - Electron is copyright (c) 2013-2023 GitHub Inc. ([MIT license][licensefile]) 73 | - Quixe is copyright (c) 2010-2023, Andrew Plotkin ([MIT license][licensefile]) 74 | - inkjs is copyright (c) 2017-2023 Yannick Lohse ([MIT license][licensefile]) 75 | - ifvms.js is copyright (c) 2016-2023 Dannii Willis and other contributors ([MIT license][licensefile]) 76 | - emglken is copyright (c) 2012-2023, Andrew Plotkin, Dannii Willis ([MIT license][licensefile]) 77 | - Git (in emglken) is copyright (c) 2003 Iain Merrick ([MIT license][licensefile]) 78 | - Glulxe (in emglken) is copyright (c) 1999-2023, Andrew Plotkin ([MIT license][licensefile]) 79 | - Hugo (in emglken) is copyright (c) 2011 by Kent Tessman ([BSD license][licensefile]) 80 | - TADS (in emglken) is copyright (c) 1991-2012 by Michael J. Roberts ([dual-licensed GPL/TADS license][licensefile]) 81 | - Scare (in emglken) is copyright (c) 2003-2008, Simon Baldwin and Mark J. Tilford ([GPL][licensefile]) 82 | - RemGlk (in emglken) is copyright (c) 2012-2023, Andrew Plotkin ([MIT license][licensefile]) 83 | 84 | [licensefile]: LICENSE 85 | 86 | # For developers 87 | 88 | If you've just downloaded the source code for this puppy, it's easy to make a runnable version. 89 | 90 | First, you need to have the [Node][] development tools installed. Everything relies on the `npm` command-line tool. See [Installing Node.js via package manager][npminstall]. 91 | 92 | [npminstall]: https://nodejs.org/en/download/package-manager/ 93 | 94 | To fetch all the necessary Node packages and place them in a `node_modules` directory: 95 | 96 | npm install 97 | 98 | This command also fetches the Quixe submodule (which will live in the `quixe` directory). You must have `git` installed for this to work. 99 | 100 | Now just type 101 | 102 | npm start 103 | 104 | ...to launch the app. 105 | 106 | When run this way, the app will show up named as "Electron", not "Lectrote". 107 | 108 | ## Packaging Lectrote 109 | 110 | The `makedist.py` script builds the zip files which you see on the [release page][release]. (Yes, it is silly to use a Python packaging script in a Node.js package. Maybe I'll rewrite it into Javascript. Later.) 111 | 112 | [release]: https://github.com/erkyrath/lectrote/releases 113 | 114 | python3 makedist.py 115 | 116 | This creates build directories and then zip files in a `dist` directory. Add `-b` to *only* generate the build dirs; `-z` to transform existing build dirs into zip files. 117 | 118 | You can add arguments to narrow down the platforms you are building, e.g.: 119 | 120 | python3 makedist.py darwin 121 | python3 makedist.py win32 122 | python3 makedist.py linux 123 | python3 makedist.py win32-x64 124 | 125 | Note that "darwin" includes "darwin-x64", "darwin-arm64", and "darwin-universal" (both packaged together). 126 | 127 | If you want to code-sign the Mac version, use the `--macsign` argument: 128 | 129 | python3 makedist.py darwin --macsign 'Developer ID Application: ...' 130 | 131 | You must be a registered Apple developer to do this. The argument must be the name of the "Developer Id Application" certificate in your keychain. Run the Keychain Access app to see this. If you don't have one, the easiest way to set it up is to run Xcode, open the Preferences, select Accounts, and hit Manage Certificates. 132 | 133 | ## Packaging a bound game 134 | 135 | You will need to create a separate directory for your game's files. Copy `package.json` to the directory, adding or modifying these lines: 136 | 137 | - `name`: A node package name. This is not used anywhere, so it doesn't really matter. 138 | - `productName`: The display name for the app. 139 | - `version`: Version number of your game. 140 | - `author`: You, the game's author. 141 | - `description`: One-line description of your game. 142 | - `lectrotePackagedGame`: Pathname to the game file. 143 | - `lectroteSoleInterpreter`: Set to `"ifvms"`, `"inkjs"`, `"emglken"` to include just one of Lectrote's interpreter engines. (Optional, but it saves a little bit of space.) (Note that Git, Glulxe, Hugo, and TADS are all handled by the emglken package. There's currently no way to include just one of them.) 144 | - `lectroteExtraFiles`: An array of extra files to include. These are assumed to be in the game directory, so you do not have to include the directory prefix. (This list must include the game file -- yes, it's redundant with `lectrotePackagedGame`.) 145 | - `lectroteMacAppID`: If you plan to build a MacOS app, a reverse-DNS ID string to uniquely identify it. 146 | - `lectroteCopyright`: Copyright string (applied to Windows binaries). 147 | 148 | (Do not change `lectroteVersion`; that should always show the Lectrote release that you built your bound app from.) 149 | 150 | To create a Mac DMG archive, you will also need a `resources/pack-dmg-spec.json` file. See `samplegame/resources/pack-dmg-spec.json`. You should update the "title", the "dist/Adventure-$PLATFORM$" paths under "contents", and (if you want) the "background" image which is used for the folder window. 151 | 152 | You may also copy any of Lectrote's content files to your game directory and customize them. You will probably want to customize `about.html`, for example. 153 | 154 | The `samplegame` directory in the Lectrote source demonstrates the layout. It will be simplest to clone that and alter it. 155 | 156 | Once your files are ready, do: 157 | 158 | python3 makedist.py --game GAMEDIR 159 | 160 | This will build and package apps for all platforms. (You can test this out of the box by using `samplegame` for the GAMEDIR.) As noted above, you can cut down the stages or targets with the `-b`, `-z` options or by naming platforms. 161 | 162 | You cannot launch a bound game by typing `npm start`. You have to package it, at least to the `-b` stage, and run it from the `dist` directory. 163 | 164 | ### Customizing your bound app 165 | 166 | As noted, you can copy `play.html`, `el-glkote.css`, or other Lectrote files into your gamedir and customize them. When packaging with the `--game` option, files found in the gamedir will replace normal Lectrote files. 167 | 168 | If you add new files (not replacing Lectrote files), be sure to list them in the `lectroteExtraFiles` array. 169 | 170 | You can extend the functionality of the app -- for example, adding or removing menu items. Add a Javascript file to your gamedir, and name it in your `package.json` file: 171 | 172 | "lectroteMainExtension": "GAMEDIR/FILE.js", 173 | 174 | (And add it to `lectroteExtraFiles` as well.) 175 | 176 | This file can define new functionality by exporting any of the following Javascript functions. For example, you could say: 177 | 178 | exports.launch = function() { ... } 179 | 180 | - `exports.launch()`: Called when the app starts up. 181 | - `exports.app_ready()`: Called when the app is ready to open windows. At this point the game window has already been opened. 182 | - `exports.construct_menu_template(template, special)`: Called to customize the app menu template. The `template` argument is a Javascript data structure as described in [the Electron Menu docs][elemenu]. `special` is null for the game window, or one of the strings `"about", "prefs", "card"` for one of Lectrote's special windows. Modify `template` and return it. 183 | - `exports.set_zoom_factor(val)`: Called when the app's zoom level changes. The argument is suitable for Electron's `setZoomFactor()` method. 184 | - `exports.set_darklight_mode(val)`: Called when the OS native theme changes. The argument is false for light theme, true for dark theme. 185 | - `exports.export_game_path()`: The bound app normally has an "Export Portable Game File..." menu option, which lets the user extract your game file for use in other interpreters. You can implement this function and return null to suppress this menu option. You can also return the pathname of a different game file, which is not actually a useful thing to do. 186 | - `exports.cover_image_info`: An object `{ url:URL, width:W, height:H }` which provides cover art. This is only needed if your game is not a blorb file. (If it is not provided, Lectrote attempts to load the blorb cover art as usual.) 187 | - `exports.about_window_size`: An object `{ width:W, height:H }` which customizes the size of the `about.html` window. (Defaults to `{ width:600, height:450 }`.) 188 | 189 | [elemenu]: https://www.electronjs.org/docs/latest/api/menu/ 190 | 191 | The main Lectrote module exports several functions you can use in your extension code. I have not yet documented them; see the `main.js` file. 192 | 193 | #### Style customizations for dark/light mode 194 | 195 | As of release 1.3.6 (August 2020), Lectrote supports OS dark theme. You should do the same for any windows you have added or customized. 196 | 197 | Look at `about.html` to see how this works. The `evhan_darklight()` function alters the document style; the `onready()` function now sets up a callback for this function. The `` tag now has `` to support this, and several `.DarkMode` stanzas have been added to the CSS. You should copy these changes in your own `about.html`. 198 | 199 | When opening a window, use a backgroundColor line to set the loading color, minimizing flash: 200 | 201 | backgroundColor: (electron.nativeTheme.shouldUseDarkColors ? '#000' : '#FFF'), 202 | 203 | Then, in the `dom-ready` event, send a message to convey the OS theme: 204 | 205 | win.webContents.send('set-darklight-mode', electron.nativeTheme.shouldUseDarkColors); 206 | 207 | Also add a `set_darklight_mode()` routine to your extension code (see above). This routine should send the same message to all open windows. 208 | 209 | In the window, set up a handler for this message and adjust your body styles appropriately: 210 | 211 | require('electron').ipcRenderer.on('set-darklight-mode', function(ev, arg) { 212 | // arg is false for light mode, true for dark mode. 213 | }); 214 | 215 | See `about.html` and `if-card.html` for examples of dark/light style handling. 216 | 217 | Be sure to test that your windows open with the appropriate theme (matching the OS theme), and also that they change dynamically when the OS theme changes. 218 | -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | About Lectrote 5 | 6 | 7 | 8 | 55 | 56 | 181 | 182 | 183 | 184 | 185 |
186 | 187 |

188 | Lectrote icon 189 |

190 | 191 |

Lectrote ???

192 |

A portable IF interpreter app

193 |

Constructed by Andrew Plotkin

194 | 195 |

196 | Quixe ???; 197 | inkjs ???; 198 | ZVM (ifvms) ???; 199 |
200 | emglken ??? 201 | (Git ???; 202 | Glulxe ???; 203 | Hugo ???; 204 | TADS ???; 205 | Scare ???) 206 |
207 | Electron ??? 208 | (Node ???, 209 | Chrome ???) 210 | — 211 | license info 212 |

213 | 214 |

215 | Select Open Game 216 | to begin playing a Glulx, Z-code, Hugo, TADS, Adrift 4, or Ink game file! 217 | 218 |

219 | 220 |
221 | 222 | 223 | 226 | 227 | -------------------------------------------------------------------------------- /apphooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AppHooks = function() { 4 | 5 | const electron = require('electron'); 6 | const path_mod = require('path'); 7 | const fs = require('fs'); 8 | 9 | const fonts = require('./fonts.js'); 10 | const searchbar = require('./searchbar.js'); 11 | const formats = require('./formats.js'); 12 | const traread = require('./traread.js'); 13 | const tragen = require('./tragen.js'); 14 | 15 | function load_named_game(arg) 16 | { 17 | const engine = formats.enginemap[arg.engine]; 18 | if (!engine) { 19 | throw new Error('Unrecognized engine: ' + arg.engine); 20 | } 21 | 22 | var path = arg.path; 23 | var default_name = path_mod.basename(path); 24 | var buf = fs.readFileSync(path); 25 | var load_options = { format:'array' }; 26 | 27 | game_options.default_page_title = default_name; 28 | game_options.game_format_name = engine.name; /* label used for loading error messages */ 29 | game_options.engine_name = engine.name; /* label used in page title */ 30 | game_options.recording_handler = tragen.record_update; 31 | 32 | var arr = null; 33 | if (engine.load) 34 | arr = engine.load(arg, buf, game_options); 35 | 36 | GiLoad.load_run(game_options, arr, load_options); 37 | /* Note that load_run() is synchronous with this config (the "not a 38 | URL at all" path). So Blorb should now be safely inited. */ 39 | var Blorb = GiLoad.getlibrary('Blorb'); 40 | if ((!Blorb) || (!Blorb.inited())) { 41 | console.log('Blorb is not inited after load_run()!'); 42 | } 43 | 44 | /* Pass some metadata back to the app. A fallback title at the 45 | very least. */ 46 | var obj = { 47 | title: default_name 48 | }; 49 | 50 | if (Blorb && Blorb.get_metadata) { 51 | for (var key of traread.metadata_keylist) { 52 | var val = Blorb.get_metadata(key); 53 | if (val) 54 | obj[key] = val; 55 | } 56 | } 57 | 58 | if (engine.get_signature) { 59 | var signature = engine.get_signature(); 60 | if (signature) 61 | obj.signature = signature; 62 | } 63 | 64 | var coverimageres = undefined; 65 | if (Blorb && Blorb.get_cover_pict) { 66 | coverimageres = Blorb.get_cover_pict(); 67 | } 68 | if (coverimageres !== undefined) { 69 | obj.coverimageres = coverimageres; 70 | } 71 | 72 | /* Also pass the info to the transcript module. */ 73 | tragen.set_transcriptdir(arg.transcriptdir); 74 | tragen.set_metadata(obj); 75 | 76 | electron.ipcRenderer.send('game_metadata', obj); 77 | } 78 | 79 | function set_clear_autosave(val) 80 | { 81 | game_options.clear_vm_autosave = val; 82 | } 83 | 84 | function set_disable_autosave(val) 85 | { 86 | game_options.do_vm_autosave = (!val); 87 | } 88 | 89 | function display_cover_art(dat) 90 | { 91 | /* If the cover art pane is already up, remove it. */ 92 | var panel = $('#cover_art_pane'); 93 | if (panel.length) { 94 | panel.remove(); 95 | return; 96 | } 97 | 98 | if (dat) { 99 | /* dat is an object containing { url, width, height } of an image. */ 100 | } 101 | else { 102 | var Blorb = GiLoad.getlibrary('Blorb'); 103 | 104 | /* dat is null; try to use the cover image data from the blorb. */ 105 | if (!(Blorb && Blorb.get_cover_pict)) 106 | return; 107 | var coverimageres = Blorb.get_cover_pict(); 108 | if (coverimageres === undefined) 109 | return; 110 | 111 | var info = Blorb.get_image_info(coverimageres); 112 | if (!info) 113 | return; 114 | var url = Blorb.get_image_url(coverimageres); 115 | 116 | dat = { url:url, width:info.width, height:info.height }; 117 | } 118 | 119 | if (!dat.url || !dat.width || !dat.height) 120 | return; 121 | 122 | var panel = $('
', { id:'cover_art_pane' }); 123 | var imgel = $('', { src:dat.url }); 124 | var heightval = Math.round(90 * dat.height / dat.width); 125 | imgel.css({ width:'90vmin', height:heightval+'vmin' }); 126 | panel.append(imgel); 127 | var inpel = $(''); 128 | inpel.val(' Hit any key '); 129 | panel.append(inpel); 130 | 131 | var removefunc = function() { 132 | $('#cover_art_pane').remove(); 133 | return true; 134 | }; 135 | panel.on('click', removefunc); 136 | inpel.on('keypress', removefunc); 137 | inpel.on('keydown', removefunc); 138 | 139 | $('#content').append(panel); 140 | inpel.focus(); 141 | } 142 | 143 | function set_zoom_factor(val) 144 | { 145 | var webFrame = electron.webFrame; 146 | webFrame.setZoomFactor(val); 147 | } 148 | 149 | function set_margin_level(val) 150 | { 151 | var str = '0px ' + (5*val) + '%'; 152 | $('#gameport').css({'margin':str}); 153 | } 154 | 155 | function set_color_theme(obj) 156 | { 157 | var val = obj.theme; 158 | var darklight_flag = obj.darklight; 159 | 160 | // System-reactive themes: 161 | if (val == 'lightdark') { 162 | val = (darklight_flag ? 'dark' : 'light'); 163 | } 164 | else if (val == 'sepiaslate') { 165 | val = (darklight_flag ? 'slate' : 'sepia'); 166 | } 167 | 168 | var bodyel = $('body'); 169 | 170 | bodyel.removeClass('SepiaTheme'); 171 | bodyel.removeClass('SlateTheme'); 172 | bodyel.removeClass('DarkTheme'); 173 | 174 | var search_body_el = searchbar.get_search_body(); 175 | 176 | if (search_body_el) { 177 | search_body_el.removeClass('SepiaTheme'); 178 | search_body_el.removeClass('SlateTheme'); 179 | search_body_el.removeClass('DarkTheme'); 180 | } 181 | 182 | switch (val) { 183 | 184 | case 'sepia': 185 | bodyel.addClass('SepiaTheme'); 186 | if (search_body_el) 187 | search_body_el.addClass('SepiaTheme'); 188 | break; 189 | 190 | case 'slate': 191 | bodyel.addClass('SlateTheme'); 192 | if (search_body_el) 193 | search_body_el.addClass('SlateTheme'); 194 | break; 195 | 196 | case 'dark': 197 | bodyel.addClass('DarkTheme'); 198 | if (search_body_el) 199 | search_body_el.addClass('DarkTheme'); 200 | break; 201 | 202 | default: 203 | /* Light theme is the default. */ 204 | break; 205 | } 206 | } 207 | 208 | function set_font(obj) 209 | { 210 | var fontline = fonts.get_fontline(obj.font, obj.customfont); 211 | 212 | var fontclass = '.BufferWindow'; 213 | /* In Parchment the buffer-div class is different, so check the 214 | game options for that. (Parchment is not part of Lectrote any 215 | more, but we keep the option.) */ 216 | if (game_options.lectrote_font_class) 217 | fontclass = game_options.lectrote_font_class; 218 | 219 | var el = $('#fontcss'); 220 | if (!fontline) { 221 | el.remove(); 222 | } 223 | else { 224 | if (!el.length) { 225 | el = $(' 185 | 186 | 190 | 191 | 192 | 193 | 194 |
195 |
Appearance
196 |
Interpreters
197 |
Transcripts
198 |
199 |
200 |
201 | 202 |

Story Font

203 | 204 |
205 | 206 | 209 |   210 | 211 | 212 |
213 | 214 |

Zoom Level

215 | 216 |
217 | 218 | 219 |   220 | -- 221 | 222 |
223 | 224 |

Color Theme

225 | 226 |
227 | 228 | 231 | 232 |
233 | 234 |

Margins

235 | 236 |
237 | 238 | 239 |   240 | -- 241 | 242 |
243 | 244 |
245 | 246 |
247 |
248 | At End Of Road
249 | You are standing at the end of a road before a small brick building. 250 | Around you is a forest. A small stream flows out of the building and 251 | down a gully. 252 | (From Colossal Cave, Crowther & Woods) 253 |
254 |
255 | 256 |
257 |
258 | 259 |

Glulx Interpreter

260 | 261 |
262 | 263 | 266 | 267 |
268 | 269 |
270 | 271 |
272 | 273 |

Retain Transcripts

274 | 275 |
276 |
277 | 278 | 279 |
280 |
281 | 282 | 285 |
286 |
287 | 288 | 291 |
292 |
293 |
294 | Deletions occur when you quit Lectrote. 295 |
296 | 297 |
298 | 299 |
300 | 301 | 302 | 303 | -------------------------------------------------------------------------------- /prefs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const electron = require('electron'); 3 | 4 | const fonts = require('./fonts.js'); 5 | const formats = require('./formats.js'); 6 | 7 | /* Code for the Preferences window. */ 8 | 9 | const tablist = [ 'appear', 'terp', 'tra' ]; 10 | 11 | var darklight_flag = false; 12 | 13 | /* Set up the initial appearance of the window. This adjusts the controls 14 | and the sample text, but does not send changes to the app (because there 15 | have been no changes yet). 16 | */ 17 | function setup_with_prefs(prefs, isbound) 18 | { 19 | var sel, optel; 20 | 21 | // Function-calling function because closures suck. 22 | var setclick = function(val) { 23 | $('#tabbutton-'+val).on('click', function(ev) { 24 | set_tab(val); 25 | ev.preventDefault(); 26 | }); 27 | } 28 | for (var ix=0; ix', { value:theme.key }).text(theme.label); 41 | if (prefs.gamewin_colortheme == theme.key) 42 | optel.prop('selected', true); 43 | sel.append(optel); 44 | } 45 | 46 | sel.on('change', evhan_color_theme); 47 | apply_color_theme(prefs.gamewin_colortheme); 48 | 49 | 50 | sel = $('#sel-font'); 51 | sel.prop('disabled', false); 52 | sel.empty(); 53 | 54 | for (var ix=0; ix', { value:font.key }).text(font.label); 57 | if (prefs.gamewin_font == font.key) 58 | optel.prop('selected', true); 59 | sel.append(optel); 60 | } 61 | 62 | sel.on('change', evhan_font); 63 | 64 | var inpel = $('#input-font'); 65 | if (prefs.gamewin_customfont) 66 | inpel.val(prefs.gamewin_customfont); 67 | inpel.on('change', evhan_font); 68 | 69 | apply_font(prefs.gamewin_font, prefs.gamewin_customfont); 70 | 71 | 72 | sel = $('#range-margin'); 73 | sel.attr('step', 1); 74 | sel.attr('min', 0); 75 | sel.attr('max', 7); 76 | 77 | sel.on('input', evhan_margin_level); 78 | sel.val(prefs.gamewin_marginlevel); 79 | apply_margin_level(prefs.gamewin_marginlevel); 80 | 81 | 82 | sel = $('#range-zoom'); 83 | sel.attr('step', 1); 84 | sel.attr('min', -6); 85 | sel.attr('max', 6); 86 | 87 | sel.on('input', evhan_zoom_level); 88 | sel.val(prefs.gamewin_zoomlevel); 89 | apply_zoom_level(prefs.gamewin_zoomlevel); 90 | 91 | 92 | if (!isbound) { 93 | sel = $('#sel-glulx-terp'); 94 | sel.prop('disabled', false); 95 | sel.empty(); 96 | 97 | for (var ix=0; ix', { value:engine.id }).text(engine.name); 100 | if (prefs.glulx_terp == engine.id) 101 | optel.prop('selected', true); 102 | sel.append(optel); 103 | } 104 | 105 | sel.on('change', evhan_glulx_terp); 106 | } 107 | else { 108 | sel = $('#tabheader'); 109 | sel.hide(); 110 | } 111 | 112 | for (var val of [ 'forever', 'count', 'time' ]) { 113 | sel = $('#retaintra-'+val); 114 | if (prefs.traretain_for == val) 115 | sel.prop('checked', true); 116 | sel.on('change', { for:val }, evhan_retain_transcripts); 117 | } 118 | 119 | sel = $('#retain-count'); 120 | sel.prop('value', prefs.traretain_count); 121 | sel.on('change', evhan_retain_count); 122 | 123 | sel = $('#retain-daycount'); 124 | sel.prop('value', prefs.traretain_daycount); 125 | sel.on('change', evhan_retain_daycount); 126 | } 127 | 128 | function set_tab(val) 129 | { 130 | switch (val) { 131 | case 'appear': 132 | $('body').addClass('CurrentTabAppear'); 133 | $('body').removeClass('CurrentTabTerp'); 134 | $('body').removeClass('CurrentTabTra'); 135 | break; 136 | case 'terp': 137 | $('body').removeClass('CurrentTabAppear'); 138 | $('body').addClass('CurrentTabTerp'); 139 | $('body').removeClass('CurrentTabTra'); 140 | break; 141 | default: 142 | $('body').removeClass('CurrentTabAppear'); 143 | $('body').removeClass('CurrentTabTerp'); 144 | $('body').addClass('CurrentTabTra'); 145 | break; 146 | } 147 | } 148 | 149 | /* The apply_... functions adjust the sample text in this window, but 150 | do not directly affect the controls or send changes to the app. 151 | 152 | A lot of this code is copied from apphooks.js. It has to be kept 153 | in sync. 154 | */ 155 | 156 | var themelist = [ 157 | { key:'lightdark', label:'System (Light/Dark)' }, 158 | { key:'sepiaslate', label:'System (Sepia/Slate)' }, 159 | { key:'light', label:'Light' }, 160 | { key:'sepia', label:'Sepia' }, 161 | { key:'slate', label:'Slate' }, 162 | { key:'dark', label:'Dark' } 163 | ]; 164 | 165 | function apply_color_theme(val) 166 | { 167 | // System-reactive themes: 168 | if (val == 'lightdark') { 169 | val = (darklight_flag ? 'dark' : 'light'); 170 | } 171 | else if (val == 'sepiaslate') { 172 | val = (darklight_flag ? 'slate' : 'sepia'); 173 | } 174 | 175 | var bodyel = $('.Sample'); 176 | 177 | bodyel.removeClass('SepiaTheme'); 178 | bodyel.removeClass('SlateTheme'); 179 | bodyel.removeClass('DarkTheme'); 180 | 181 | switch (val) { 182 | 183 | case 'sepia': 184 | bodyel.addClass('SepiaTheme'); 185 | break; 186 | 187 | case 'slate': 188 | bodyel.addClass('SlateTheme'); 189 | break; 190 | 191 | case 'dark': 192 | bodyel.addClass('DarkTheme'); 193 | break; 194 | 195 | default: 196 | /* Light theme is the default. */ 197 | break; 198 | } 199 | } 200 | 201 | var fontlist = [ 202 | { key:'lora', label:'Lora' }, 203 | { key:'gentium', label:'Gentium Book' }, 204 | { key:'georgia', label:'Georgia' }, 205 | { key:'baskerville', label:'Libre Baskerville' }, 206 | { key:'helvetica', label:'Helvetica' }, 207 | { key:'sourcesanspro', label:'Source Sans Pro' }, 208 | { key:'courier', label:'Courier' }, 209 | { key:'custom', label:'Other Font...' } 210 | ]; 211 | 212 | function apply_font(fontkey, customfont) 213 | { 214 | var inpel = $('#input-font'); 215 | 216 | if (fontkey == 'custom') { 217 | if (inpel.css('display') != 'inline-block') { 218 | inpel.css('display', 'inline-block'); 219 | inpel.select(); 220 | inpel.focus(); 221 | } 222 | } 223 | else { 224 | if (inpel.css('display') != 'none') { 225 | inpel.css('display', 'none'); 226 | } 227 | } 228 | 229 | //### check if anything's changed 230 | var fontline = fonts.get_fontline(fontkey, customfont); 231 | 232 | var el = $('#fontcss'); 233 | if (!fontline) { 234 | el.remove(); 235 | } 236 | else { 237 | if (!el.length) { 238 | el = $(' 43 | 44 | 112 | 113 | 114 | 115 | 116 |
117 | 118 |

119 | Lectrote icon 120 |

121 | 122 |

Adventure

123 |

The Interactive Original

124 |

By Will Crowther (1973) and Don Woods (1977)

125 |

Mac/Win/Linux release ???

126 | 127 |

128 | Interpreter framework: Lectrote ??? 129 |

130 |

131 | Quixe version ???; 132 | Electron version ??? 133 | (Node ???, 134 | Chrome ???) 135 |

136 | 137 |
138 | 139 | 140 | 143 | 144 | -------------------------------------------------------------------------------- /samplegame/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adventure", 3 | "productName": "Adventure", 4 | "version": "1.0.0", 5 | "lectroteVersion": "1.5.4", 6 | "lectrotePackagedGame": "samplegame/Advent.ulx", 7 | "lectroteMacAppID": "com.eblong.adventsample", 8 | "lectroteCopyright": "Written by Will Crowther and Don Woods", 9 | "description": "The original adventure game", 10 | "lectroteExtraFiles": [ 11 | "Advent.ulx" 12 | ], 13 | "main": "main.js", 14 | "scripts": { 15 | "start": "electron main.js", 16 | "preinstall": "if [ -f quixe/LICENSE ]; then echo Quixe already installed; elif [ -d .git ]; then git submodule init; git submodule update; else git clone https://github.com/erkyrath/quixe.git; fi" 17 | }, 18 | "author": "Andrew Plotkin ", 19 | "license": "MIT", 20 | "dependencies": { 21 | "electron": "^34.5.0" 22 | }, 23 | "devDependencies": { 24 | "electron-packager": "^17.1.0", 25 | "@electron/universal": "^1.0" 26 | }, 27 | "optionalDependencies": { 28 | "appdmg": "^0.6.6" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /samplegame/play.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Adventure 5 | 6 | 7 | 8 | 9 | 10 | 46 | 47 | 48 | 49 | 50 | 51 |
52 |
53 |
54 | 57 |
58 |
59 | LOADING
60 |    Loading... 61 |
62 | 63 |
64 | 65 |
66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /samplegame/resources/pack-dmg-spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Install Adventure", 3 | "icon": "../../resources/appicon-dmg.icns", 4 | "background": "../../resources/pack-dmg-background.png", 5 | "icon-size": 80, 6 | "contents": [ 7 | { "x": 448, "y": 200, "type": "link", "path": "/Applications" }, 8 | { "x": 192, "y": 200, "type": "file", "path": "../../dist/Adventure-$PLATFORM$/Adventure.app" }, 9 | { "x": 160, "y": 360, "type": "file", "path": "../../dist/Adventure-$PLATFORM$/LICENSE" }, 10 | { "x": 320, "y": 360, "type": "file", "path": "../../dist/Adventure-$PLATFORM$/LICENSES-FONTS.txt" }, 11 | { "x": 480, "y": 360, "type": "file", "path": "../../dist/Adventure-$PLATFORM$/LICENSES.chromium.html" } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /searchbar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const electron = require('electron'); 4 | 5 | /* Manage the search UI for both game and trashow windows. 6 | 7 | This assumes that the document has a #searchbar div. 8 | (For various reasons, this div must have the .CanHaveInputFocus 9 | class.) 10 | 11 | The search process is a rather confusing back-and-forth between this 12 | code (in the game window) and main.js. Note that main.js has a bunch 13 | of handlers defined for both game and trashow objects, and those 14 | objects share the search-related properties. 15 | */ 16 | 17 | var search_input_el = null; 18 | var search_body_el = null; 19 | 20 | /* Construct the search UI in the shadow DOM. */ 21 | function construct_searchbar() 22 | { 23 | var barel = $('#searchbar'); 24 | if (!barel || !barel.length) 25 | return; 26 | 27 | barel.empty(); 28 | var shadow = barel.get(0).attachShadow({ mode: 'open' }); 29 | 30 | var bodyel = $('
', { id:'searchbar_body' }); 31 | search_body_el = bodyel; 32 | 33 | var inputel = $('', { id:'searchbar_input', type:'text' }); 34 | search_input_el = inputel; 35 | var prevel = $('