' + rowCounter + '. ';
260 | }
261 | return rowAppend;
262 | });
263 |
264 | /// finaly return the html result
265 | return result;
266 | };
267 | };
268 |
--------------------------------------------------------------------------------
/src/js/classes/settings.js:
--------------------------------------------------------------------------------
1 | // Get the mechanism to use for storage.
2 | const getStorage = function() {
3 | // if `window.vsCodeApi` exists, we're in the context of the VSCode extension
4 | // which handles all of the settings internally, so we don't need to do anything here
5 | if (window.vsCodeApi) {
6 | return {
7 | getItem: () => {},
8 | setItem: () => {},
9 | };
10 | } else {
11 | return window.localStorage;
12 | }
13 | };
14 |
15 | export const Settings = function(app) {
16 | const self = this;
17 | const storage = getStorage();
18 | this.storage = storage;
19 |
20 | ko.extenders.persist = function(target, option) {
21 | target.subscribe(function(newValue) {
22 | storage.setItem(option, newValue);
23 | app.data.db.save(option, newValue);
24 | });
25 | return target;
26 | };
27 |
28 | // apply
29 | //
30 | // Applies the current settings
31 | this.apply = function() {
32 | app.setTheme(self.theme());
33 | app.setLanguage(self.language());
34 | app.setDocumentType(self.documentType());
35 | app.toggleInvertColors();
36 | app.setMarkupLanguage(self.markupLanguage());
37 | console.log('redraw throttle:', self.redrawThrottle());
38 | app.workspace.setThrottle(self.redrawThrottle());
39 | app.setGistCredentials({
40 | token: self.gistToken(),
41 | file:
42 | self.gistFile() !== null
43 | ? self
44 | .gistFile()
45 | .split('/')
46 | .pop()
47 | : null,
48 | });
49 | app.setGistPluginsFile(
50 | self.gistPluginsFile() !== null
51 | ? self
52 | .gistPluginsFile()
53 | .split('/')
54 | .pop()
55 | : null
56 | );
57 | };
58 |
59 | this.pasteFromClipboard = function(koItem){
60 | if(koItem && koItem.length > 0) return;
61 | if(!navigator.clipboard) return;
62 | navigator.clipboard
63 | .readText()
64 | .then(
65 | (clipText) => {
66 | app.log("clipboard", clipText)
67 | if(clipText && clipText.length > 5) koItem(clipText)
68 | },
69 | );
70 | }
71 | this.guessGistToken = function() {
72 | self.pasteFromClipboard(self.gistToken);
73 | }
74 | this.guessGistFile = function() {
75 | self.pasteFromClipboard(self.gistFile);
76 | }
77 |
78 | this.validateGridSize = function() {
79 | if (self.gridSize() < 20) {
80 | self.gridSize(20);
81 | }
82 |
83 | if (self.gridSize() > 200) {
84 | self.gridSize(200);
85 | }
86 | self.gridSize(parseInt(self.gridSize()));
87 | app.initGrid();
88 | };
89 |
90 | // Theme
91 | this.theme = ko
92 | .observable(storage.getItem('theme') || 'dracula')
93 | .extend({ persist: 'theme' });
94 |
95 | // Document type
96 | this.documentType = ko
97 | .observable(storage.getItem('documentType') || 'yarn')
98 | .extend({ persist: 'documentType' });
99 |
100 | // Language
101 | this.language = ko
102 | .observable(storage.getItem('language') || 'en-GB')
103 | .extend({ persist: 'language' });
104 |
105 | // Redraw throttle
106 | this.redrawThrottle = ko
107 | .observable(parseInt(storage.getItem('redrawThrottle') || '130'))
108 | .extend({ persist: 'redrawThrottle' });
109 |
110 | this.gistToken = ko
111 | .observable(storage.getItem('gistToken'))
112 | .extend({ persist: 'gistToken' });
113 |
114 | this.gistFile = ko
115 | .observable(storage.getItem('gistFile'))
116 | .extend({ persist: 'gistFile' });
117 |
118 | this.lastEditedGist = ko
119 | .observable(storage.getItem('lastEditedGist'))
120 | .extend({ persist: 'lastEditedGist' });
121 |
122 | this.lastStorageHost = ko
123 | .observable(storage.getItem('lastStorageHost'))
124 | .extend({ persist: 'lastStorageHost' });
125 |
126 | this.gistPluginsFile = ko
127 | .observable(storage.getItem('gistPluginsFile'))
128 | .extend({ persist: 'gistPluginsFile' });
129 |
130 | // Spellcheck enabled
131 | this.spellcheckEnabled = ko
132 | .observable(
133 | storage.getItem('spellcheckEnabled') !== null
134 | ? storage.getItem('spellcheckEnabled') === 'true'
135 | : true
136 | )
137 | .extend({ persist: 'spellcheckEnabled' });
138 |
139 | // Auto Close Tags
140 | this.autoCloseTags = ko
141 | .observable(
142 | storage.getItem('autoCloseTags') !== null
143 | ? storage.getItem('autoCloseTags') === 'true'
144 | : true
145 | )
146 | .extend({ persist: 'autoCloseTags' });
147 |
148 | // Autocomplete Suggestions
149 | this.autocompleteSuggestionsEnabled = ko
150 | .observable(
151 | storage.getItem('autocompleteSuggestionsEnabled') !== null
152 | ? storage.getItem('autocompleteSuggestionsEnabled') === 'true'
153 | : true
154 | )
155 | .extend({ persist: 'autocompleteSuggestionsEnabled' });
156 |
157 | // Auto Close Brackets
158 | this.autoCloseBrackets = ko
159 | .observable(
160 | storage.getItem('autoCloseBrackets') !== null
161 | ? storage.getItem('autoCloseBrackets') === 'true'
162 | : true
163 | )
164 | .extend({ persist: 'autoCloseBrackets' });
165 |
166 | // Night mode
167 | this.invertColorsEnabled = ko
168 | .observable(
169 | storage.getItem('invertColorsEnabled') !== null
170 | ? storage.getItem('invertColorsEnabled') === 'true'
171 | : false
172 | )
173 | .extend({ persist: 'invertColorsEnabled' });
174 |
175 | // Snap to grid
176 | this.snapGridEnabled = ko
177 | .observable(
178 | storage.getItem('snapGridEnabled') !== null
179 | ? storage.getItem('snapGridEnabled') === 'true'
180 | : false
181 | )
182 | .extend({ persist: 'snapGridEnabled' });
183 |
184 | this.restoreSessionEnabled = ko
185 | .observable(
186 | storage.getItem('restoreSessionEnabled') !== null
187 | ? storage.getItem('restoreSessionEnabled') === 'true'
188 | : false
189 | )
190 | .extend({ persist: 'restoreSessionEnabled' });
191 |
192 | this.developmentModeEnabled = ko
193 | .observable(
194 | storage.getItem('developmentModeEnabled') !== null
195 | ? storage.getItem('developmentModeEnabled') === 'true'
196 | : false
197 | )
198 | .extend({ persist: 'developmentModeEnabled' });
199 |
200 | // Grid size
201 | this.gridSize = ko
202 | .observable(parseInt(storage.getItem('gridSize') || '40'))
203 | .extend({ persist: 'gridSize' });
204 |
205 | // Autocreate nodes
206 | this.createNodesEnabled = ko
207 | .observable(
208 | storage.getItem('createNodesEnabled') !== null
209 | ? storage.getItem('createNodesEnabled') === 'true'
210 | : true
211 | )
212 | .extend({ persist: 'createNodesEnabled' });
213 |
214 | // Editor stats
215 | this.editorStatsEnabled = ko
216 | .observable(
217 | storage.getItem('editorStatsEnabled') !== null
218 | ? storage.getItem('editorStatsEnabled') === 'true'
219 | : false
220 | )
221 | .extend({ persist: 'editorStatsEnabled' });
222 |
223 | // Markup language
224 | this.markupLanguage = ko
225 | .observable(storage.getItem('markupLanguage') || 'bbcode')
226 | .extend({ persist: 'markupLanguage' });
227 |
228 | // Filetype version
229 | this.filetypeVersion = ko
230 | .observable(storage.getItem('filetypeVersion') || '1')
231 | .extend({ persist: 'filetypeVersion' });
232 |
233 | // Line Style
234 | this.lineStyle = ko
235 | .observable(storage.getItem('lineStyle') || 'straight')
236 | .extend({ persist: 'lineStyle' });
237 |
238 | this.fileTabsVisible = ko
239 | .observable(
240 | storage.getItem('fileTabsVisible') !== null
241 | ? storage.getItem('fileTabsVisible') === 'true'
242 | : true
243 | )
244 | .extend({ persist: 'fileTabsVisible' });
245 |
246 | this.selectedFileTab = ko
247 | .observable(storage.getItem('selectedFileTab') || 0)
248 | .extend({ persist: 'selectedFileTab' });
249 |
250 | // Always open nodes in Visual Studio Code Editor
251 | // We don't actually show this in the settings menu; it can only be set by the VSCode extension's settings
252 | this.alwaysOpenNodesInVisualStudioCodeEditor = ko
253 | .observable(
254 | storage.getItem('alwaysOpenNodesInVisualStudioCodeEditor') !== null
255 | ? storage.getItem('alwaysOpenNodesInVisualStudioCodeEditor') === 'true'
256 | : false
257 | )
258 | .extend({ persist: 'alwaysOpenNodesInVisualStudioCodeEditor' });
259 |
260 | this.editorSplitDirection = ko
261 | .observable(storage.getItem('editorSplitDirection') || 'left')
262 | .extend({ persist: 'editorSplitDirection' });
263 |
264 | this.editorSplit = ko
265 | .observable(
266 | storage.getItem('editorSplit') !== null
267 | ? storage.getItem('editorSplit') === 'true'
268 | : false
269 | )
270 | .extend({ persist: 'editorSplit' });
271 |
272 | this.editorSplitSize = ko
273 | .observable(storage.getItem('editorSplitSize') || '50%')
274 | .extend({ persist: 'editorSplitSize' });
275 | };
276 |
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | import '../scss/jquery.contextMenu.css';
2 | import '../scss/normalize.css';
3 | import '../scss/spectrum.css';
4 | import '../scss/style.css';
5 |
6 | import '../public/web-components.js'
7 |
8 | import { Utils } from './classes/utils';
9 |
10 | import ko from 'knockout';
11 | window.ko = ko;
12 |
13 | window.$ = window.jQuery = require('jquery');
14 | import 'jquery-contextmenu';
15 | import 'jquery-mousewheel';
16 | import 'jquery-resizable-dom';
17 |
18 | import ace from 'ace-builds/src-noconflict/ace';
19 | window.ace = ace;
20 | ace.config.set('basePath', Utils.getPublicPath()); //needed to import yarn mode
21 | window.define = ace.define;
22 |
23 | import 'ace-builds/src-min-noconflict/ext-language_tools';
24 | import 'ace-builds/src-min-noconflict/ext-searchbox';
25 | import './libs/knockout.ace.js';
26 | import 'jquery.transit';
27 |
28 | import 'spectrum-colorpicker';
29 | import 'lightweight-emoji-picker/dist/picker.js';
30 |
31 | // Keep these imports, they are used elsewhere in the app
32 | import Swal from 'sweetalert2';
33 | window.Swal = Swal;
34 |
35 | import { App } from './classes/app.js';
36 | import { version } from '../public/version.json';
37 |
38 | // Register PWA service worker
39 | if ('serviceWorker' in navigator) {
40 | window.addEventListener('load', () => {
41 | navigator.serviceWorker
42 | .register('sw.js')
43 | .then(registration => {
44 | // registration.pushManager.subscribe({userVisibleOnly: true});
45 | console.log('SW registered: ', registration);
46 | })
47 | .catch(registrationError => {
48 | console.log('SW registration failed: ', registrationError);
49 | });
50 | });
51 | }
52 |
53 | window.app = new App('Yarn', version);
54 | window.app.run();
55 |
56 | // Register plugins from plugin folder
57 | import { Plugins } from '../public/plugins';
58 | const appPlugins = new Plugins(window.app);
59 | window.app.plugins = appPlugins;
60 |
--------------------------------------------------------------------------------
/src/js/libs/knockout.ace.js:
--------------------------------------------------------------------------------
1 | // custom fork of this library due to the original using brance and being unmaintained atm
2 | (function() {
3 | var instances_by_id = {}, // needed for referencing instances during updates.
4 | init_id = 0; // generated id increment storage
5 |
6 | ko.bindingHandlers.ace = {
7 | init: function(
8 | element,
9 | valueAccessor,
10 | allBindingsAccessor,
11 | viewModel,
12 | bindingContext
13 | ) {
14 | var options = allBindingsAccessor().aceOptions || {};
15 | var value = ko.utils.unwrapObservable(valueAccessor());
16 |
17 | // Ace attaches to the element by DOM id, so we need to make one for the element if it doesn't have one already.
18 | if (!element.id) {
19 | element.id = 'knockout-ace-' + init_id;
20 | init_id = init_id + 1;
21 | }
22 |
23 | var editor = ace.edit(element.id);
24 |
25 | if (options.theme) editor.setTheme('ace/theme/' + options.theme);
26 | if (options.mode) editor.getSession().setMode('ace/mode/' + options.mode);
27 |
28 | editor.setValue(value);
29 | editor.gotoLine(0);
30 | editor.setShowPrintMargin(false);
31 | editor.getSession().setUseWrapMode(true);
32 |
33 | editor.getSession().on('change', function(delta) {
34 | if (ko.isWriteableObservable(valueAccessor())) {
35 | valueAccessor()(editor.getValue());
36 | }
37 | });
38 |
39 | instances_by_id[element.id] = editor;
40 |
41 | // destroy the editor instance when the element is removed
42 | ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
43 | try {
44 | editor.destroy();
45 | } catch (e) {}
46 | delete instances_by_id[element.id];
47 | });
48 | },
49 | update: function(
50 | element,
51 | valueAccessor,
52 | allBindingsAccessor,
53 | viewModel,
54 | bindingContext
55 | ) {
56 | var value = ko.utils.unwrapObservable(valueAccessor());
57 | var id = element.id;
58 |
59 | //handle programmatic updates to the observable
60 | // also makes sure it doesn't update it if it's the same.
61 | // otherwise, it will reload the instance, causing the cursor to jump.
62 | if (id !== undefined && id !== '' && instances_by_id.hasOwnProperty(id)) {
63 | var editor = instances_by_id[id];
64 | var content = editor.getValue();
65 | if (content !== value) {
66 | editor.setValue(value);
67 | editor.gotoLine(0);
68 | }
69 | }
70 | },
71 | };
72 |
73 | ko.aceEditors = {
74 | resizeAll: function() {
75 | for (var id in instances_by_id) {
76 | if (!instances_by_id.hasOwnProperty(id)) continue;
77 | var editor = instances_by_id[id];
78 | editor.resize();
79 | }
80 | },
81 | get: function(id) {
82 | return instances_by_id[id];
83 | },
84 | };
85 | })();
86 |
--------------------------------------------------------------------------------
/src/js/libs/spellcheck_ace.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jquery/no-ajax */
2 | // You also need to load in nspell.js and jquery.js
3 |
4 | // This is a custom made fork that uses nspell instead of typo.js due to major performance issues in the later.
5 | // Please keep this file for now...
6 | var nspell = require('nspell');
7 | // You should configure these classes.
8 | var editor = 'editor'; // This should be the id of your editor element.
9 |
10 | var utils = require('../classes/utils');
11 |
12 | var dicPath = utils.Utils.getPublicPath('dictionaries/en/index.dic');
13 | var affPath = utils.Utils.getPublicPath('dictionaries/en/index.aff');
14 | // var dicPath =
15 | // "https://raw.githubusercontent.com/elastic/hunspell/master/dicts/en_US/en_US.dic";
16 | // var affPath =
17 | // "https://raw.githubusercontent.com/elastic/hunspell/master/dicts/en_US/en_US.aff";
18 |
19 | // Make red underline for gutter and words.
20 | $(
21 | ""
22 | ).appendTo('head');
23 | $(
24 | ""
25 | ).appendTo('head');
26 |
27 | // Load the dictionary.
28 | // We have to load the dictionary files sequentially to ensure
29 | var dictionary = null;
30 |
31 | function load_dictionary(dicLanguage) {
32 | console.info(`Loading ${dicLanguage} hunspell dictionary locally`);
33 | dicPath = utils.Utils.getPublicPath(`dictionaries/${dicLanguage}/index.dic`);
34 | affPath = utils.Utils.getPublicPath(`dictionaries/${dicLanguage}/index.aff`);
35 |
36 | $.get(dicPath, function(data) {
37 | dicData = data;
38 | })
39 | .fail(function() {
40 | const cachedAffData = sessionStorage.getItem('affData');
41 | const cachedDicData = sessionStorage.getItem('dicData');
42 | if (cachedAffData && cachedDicData) {
43 | console.info(
44 | `${dicLanguage} found in sessionStorage. Loading dictionary from cache...`
45 | );
46 | dictionary = new nspell(cachedAffData, cachedDicData);
47 | contents_modified = true;
48 | return;
49 | }
50 | console.error(
51 | `${dicLanguage} not found locally. Loading dictionary from server instead...`
52 | );
53 | dicPath = `https://raw.githubusercontent.com/wooorm/dictionaries/main/dictionaries/${dicLanguage}/index.dic`;
54 | affPath = `https://raw.githubusercontent.com/wooorm/dictionaries/main/dictionaries/${dicLanguage}/index.aff`;
55 |
56 | $.get(dicPath, function(data) {
57 | dicData = data;
58 | }).done(function() {
59 | $.get(affPath, function(data) {
60 | affData = data;
61 | }).done(function() {
62 | sessionStorage.setItem('affData', affData);
63 | sessionStorage.setItem('dicData', dicData);
64 | dictionary = new nspell(affData, dicData);
65 | contents_modified = true;
66 | });
67 | });
68 | })
69 | .done(function() {
70 | $.get(affPath, function(data) {
71 | affData = data;
72 | }).done(function() {
73 | console.log('Dictionary loaded locally');
74 | dictionary = new nspell(affData, dicData);
75 | contents_modified = true;
76 | });
77 | });
78 | }
79 | exports.load_dictionary = load_dictionary;
80 |
81 | // Check the spelling of a line, and return [start, end]-pairs for misspelled words.
82 | function misspelled(line) {
83 | var multiLangualNonWords = /\s+|\.|\,|\?|\\|\/|\!|\[|\]|"|'|;|:|`|\+|\-|\&|\$|@|~|#|>|<|_|\)|\(|£|\^|%|\*|„|“|\||[0-9]+/g;
84 | var words = line.split(multiLangualNonWords);
85 | // console.log(words);
86 | var i = 0;
87 | var bads = [];
88 | for (word in words) {
89 | var checkWord = words[word];
90 | if (!dictionary.correct(checkWord)) {
91 | bads[bads.length] = [i, i + words[word].length];
92 | }
93 | i += words[word].length + 1;
94 | }
95 | return bads;
96 | }
97 | exports.misspelled = misspelled;
98 |
99 | var contents_modified = true;
100 |
101 | var currently_spellchecking = false;
102 |
103 | var markers_present = [];
104 |
105 | // Spell check the Ace editor contents.
106 | function spell_check() {
107 | // Wait for the dictionary to be loaded.
108 | if (dictionary == null) {
109 | return;
110 | }
111 |
112 | if (currently_spellchecking) {
113 | return;
114 | }
115 |
116 | if (!contents_modified) {
117 | return;
118 | }
119 | currently_spellchecking = true;
120 | var session = ace.edit(editor).getSession();
121 |
122 | // Clear all markers and gutter
123 | clear_spellcheck_markers();
124 | // Populate with markers and gutter
125 | try {
126 | var Range = ace.require('ace/range').Range;
127 | var lines = session.getDocument().getAllLines();
128 | for (var i in lines) {
129 | // Check spelling of this line.
130 | var misspellings = misspelled(lines[i]);
131 |
132 | // Add markers and gutter markings.
133 | // if (misspellings.length > 0) {
134 | // session.addGutterDecoration(i, "misspelled");
135 | // }
136 | for (var j in misspellings) {
137 | var range = new Range(i, misspellings[j][0], i, misspellings[j][1]);
138 | markers_present[markers_present.length] = session.addMarker(
139 | range,
140 | 'misspelled',
141 | 'typo',
142 | true
143 | );
144 | }
145 | }
146 | } finally {
147 | currently_spellchecking = false;
148 | contents_modified = false;
149 | }
150 | }
151 | exports.spell_check = spell_check;
152 |
153 | var spellcheckEnabled = false;
154 | function enable_spellcheck() {
155 | spellcheckEnabled = true;
156 | ace
157 | .edit(editor)
158 | .getSession()
159 | .on('change', function(e) {
160 | if (spellcheckEnabled) {
161 | contents_modified = true;
162 | spell_check();
163 | }
164 | });
165 | // needed to trigger update once without input
166 | contents_modified = true;
167 | spell_check();
168 | }
169 | exports.enable_spellcheck = enable_spellcheck;
170 |
171 | function disable_spellcheck() {
172 | spellcheckEnabled = false;
173 | // Clear the markers
174 | clear_spellcheck_markers();
175 | }
176 | exports.disable_spellcheck = disable_spellcheck;
177 |
178 | function clear_spellcheck_markers() {
179 | var session = ace.edit(editor).getSession();
180 | for (var i in markers_present) {
181 | session.removeMarker(markers_present[i]);
182 | }
183 | markers_present = [];
184 | // Clear the gutter
185 | var lines = session.getDocument().getAllLines();
186 | for (var i in lines) {
187 | session.removeGutterDecoration(i, 'misspelled');
188 | }
189 | }
190 | exports.clear_spellcheck_markers = clear_spellcheck_markers;
191 |
192 | function suggest_word_for_misspelled(misspelledWord) {
193 | var array_of_suggestions = dictionary.suggest(misspelledWord);
194 | if (array_of_suggestions.length === 0) {
195 | return false;
196 | }
197 | return array_of_suggestions;
198 | }
199 | exports.suggest_word_for_misspelled = suggest_word_for_misspelled;
200 |
--------------------------------------------------------------------------------
/src/public/droid-sans-mono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/droid-sans-mono.ttf
--------------------------------------------------------------------------------
/src/public/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/icon.ico
--------------------------------------------------------------------------------
/src/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/icon.png
--------------------------------------------------------------------------------
/src/public/images/dropbox.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/images/dropbox.ico
--------------------------------------------------------------------------------
/src/public/images/inky-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/images/inky-icon.png
--------------------------------------------------------------------------------
/src/public/images/pixel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/images/pixel.png
--------------------------------------------------------------------------------
/src/public/images/renpy-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/images/renpy-128.png
--------------------------------------------------------------------------------
/src/public/images/twine-favicon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/public/images/twine-favicon-152.png
--------------------------------------------------------------------------------
/src/public/libs/uFuzzy.iife.min.js:
--------------------------------------------------------------------------------
1 | /*! https://github.com/leeoniya/uFuzzy (v1.0.14) */
2 | var uFuzzy=function(){"use strict";const e=new Intl.Collator("en",{numeric:!0,sensitivity:"base"}).compare,t=1/0,l=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n="eexxaacctt",r=/\p{P}/gu,i=(e,t,l)=>e.replace("A-Z",t).replace("a-z",l),s={unicode:!1,alpha:null,interSplit:"[^A-Za-z\\d']+",intraSplit:"[a-z][A-Z]",interBound:"[^A-Za-z\\d]",intraBound:"[A-Za-z]\\d|\\d[A-Za-z]|[a-z][A-Z]",interLft:0,interRgt:0,interChars:".",interIns:t,intraChars:"[a-z\\d']",intraIns:null,intraContr:"'[a-z]{1,2}\\b",intraMode:0,intraSlice:[1,t],intraSub:null,intraTrn:null,intraDel:null,intraFilt:()=>!0,sort:(t,l)=>{let{idx:n,chars:r,terms:i,interLft2:s,interLft1:a,start:g,intraIns:u,interIns:f}=t;return n.map(((e,t)=>t)).sort(((t,h)=>r[h]-r[t]||u[t]-u[h]||i[h]+s[h]+.5*a[h]-(i[t]+s[t]+.5*a[t])||f[t]-f[h]||g[t]-g[h]||e(l[n[t]],l[n[h]])))}},a=(e,l)=>0==l?"":1==l?e+"??":l==t?e+"*?":e+`{0,${l}}?`,g="(?:\\b|_)";function u(e){e=Object.assign({},s,e);let{unicode:t,interLft:u,interRgt:f,intraMode:c,intraSlice:o,intraIns:p,intraSub:d,intraTrn:m,intraDel:x,intraContr:b,intraSplit:R,interSplit:L,intraBound:A,interBound:S,intraChars:z}=e;p??=c,d??=c,m??=c,x??=c;let E=e.letters??e.alpha;if(null!=E){let e=E.toLocaleUpperCase(),t=E.toLocaleLowerCase();L=i(L,e,t),R=i(R,e,t),S=i(S,e,t),A=i(A,e,t),z=i(z,e,t),b=i(b,e,t)}let I=t?"u":"";const C='".+?"',y=RegExp(C,"gi"+I),k=RegExp(`(?:\\s+|^)-(?:${z}+|${C})`,"gi"+I);let{intraRules:j}=e;null==j&&(j=e=>{let t=s.intraSlice,l=0,n=0,r=0,i=0;if(/[^\d]/.test(e)){let s=e.length;s>4?(t=o,l=p,n=d,r=m,i=x):3>s||(r=Math.min(m,1),4==s&&(l=Math.min(p,1)))}return{intraSlice:t,intraIns:l,intraSub:n,intraTrn:r,intraDel:i}});let Z=!!R,$=RegExp(R,"g"+I),w=RegExp(L,"g"+I),M=RegExp("^"+L+"|"+L+"$","g"+I),B=RegExp(b,"gi"+I);const D=e=>{let t=[];e=(e=e.replace(y,(e=>(t.push(e),n)))).replace(M,"").toLocaleLowerCase(),Z&&(e=e.replace($,(e=>e[0]+" "+e[1])));let l=0;return e.split(w).filter((e=>""!=e)).map((e=>e===n?t[l++]:e))},T=/[^\d]+|\d+/g,F=(t,n=0,r=!1)=>{let i=D(t);if(0==i.length)return[];let s,h=Array(i.length).fill("");if(i=i.map(((e,t)=>e.replace(B,(e=>(h[t]=e,""))))),1==c)s=i.map(((e,t)=>{if('"'===e[0])return l(e.slice(1,-1));let n="";for(let l of e.matchAll(T)){let e=l[0],{intraSlice:r,intraIns:i,intraSub:s,intraTrn:g,intraDel:u}=j(e);if(i+s+g+u==0)n+=e+h[t];else{let[l,f]=r,c=e.slice(0,l),o=e.slice(f),p=e.slice(l,f);1==i&&1==c.length&&c!=p[0]&&(c+="(?!"+c+")");let d=p.length,m=[e];if(s)for(let e=0;d>e;e++)m.push(c+p.slice(0,e)+z+p.slice(e+1)+o);if(g)for(let e=0;d-1>e;e++)p[e]!=p[e+1]&&m.push(c+p.slice(0,e)+p[e+1]+p[e]+p.slice(e+2)+o);if(u)for(let e=0;d>e;e++)m.push(c+p.slice(0,e+1)+"?"+p.slice(e+1)+o);if(i){let e=a(z,1);for(let t=0;d>t;t++)m.push(c+p.slice(0,t)+e+p.slice(t)+o)}n+="(?:"+m.join("|")+")"+h[t]}}return n}));else{let e=a(z,p);2==n&&p>0&&(e=")("+e+")("),s=i.map(((t,n)=>'"'===t[0]?l(t.slice(1,-1)):t.split("").map(((e,t,l)=>(1==p&&0==t&&l.length>1&&e!=l[t+1]&&(e+="(?!"+e+")"),e))).join(e)+h[n]))}let o=2==u?g:"",d=2==f?g:"",m=d+a(e.interChars,e.interIns)+o;return n>0?r?s=o+"("+s.join(")"+d+"|"+o+"(")+")"+d:(s="("+s.join(")("+m+")(")+")",s="(.??"+o+")"+s+"("+d+".*)"):(s=s.join(m),s=o+s+d),[RegExp(s,"i"+I),i,h]},O=(e,t,l)=>{let[n]=F(t);if(null==n)return null;let r=[];if(null!=l)for(let t=0;l.length>t;t++){let i=l[t];n.test(e[i])&&r.push(i)}else for(let t=0;e.length>t;t++)n.test(e[t])&&r.push(t);return r};let v=!!A,U=RegExp(S,I),N=RegExp(A,I);const P=(t,l,n)=>{let[r,i,s]=F(n,1),[a]=F(n,2),g=i.length,h=t.length,c=Array(h).fill(0),o={idx:Array(h),start:c.slice(),chars:c.slice(),terms:c.slice(),interIns:c.slice(),intraIns:c.slice(),interLft2:c.slice(),interRgt2:c.slice(),interLft1:c.slice(),interRgt1:c.slice(),ranges:Array(h)},p=1==u||1==f,d=0;for(let n=0;t.length>n;n++){let h=l[t[n]],c=h.match(r),m=c.index+c[1].length,x=m,b=!1,R=0,L=0,A=0,S=0,z=0,E=0,C=0,y=0,k=[];for(let t=0,l=2;g>t;t++,l+=2){let n=c[l].toLocaleLowerCase(),r=i[t],a='"'==r[0]?r.slice(1,-1):r+s[t],o=a.length,d=n.length,j=n==a;if(!j&&c[l+1].length>=o){let e=c[l+1].toLocaleLowerCase().indexOf(a);e>-1&&(k.push(x,d,e,o),x+=_(c,l,e,o),n=a,d=o,j=!0,0==t&&(m=x))}if(p||j){let e=x-1,r=x+d,i=!1,s=!1;if(-1==e||U.test(h[e]))j&&R++,i=!0;else{if(2==u){b=!0;break}if(v&&N.test(h[e]+h[e+1]))j&&L++,i=!0;else if(1==u){let e=c[l+1],r=x+d;if(e.length>=o){let s,g=0,u=!1,f=RegExp(a,"ig"+I);for(;s=f.exec(e);){g=s.index;let e=r+g,t=e-1;if(-1==t||U.test(h[t])){R++,u=!0;break}if(N.test(h[t]+h[e])){L++,u=!0;break}}u&&(i=!0,k.push(x,d,g,o),x+=_(c,l,g,o),n=a,d=o,j=!0,0==t&&(m=x))}if(!i){b=!0;break}}}if(r==h.length||U.test(h[r]))j&&A++,s=!0;else{if(2==f){b=!0;break}if(v&&N.test(h[r-1]+h[r]))j&&S++,s=!0;else if(1==f){b=!0;break}}j&&(z+=o,i&&s&&E++)}if(d>o&&(y+=d-o),t>0&&(C+=c[l-1].length),!e.intraFilt(a,n,x)){b=!0;break}g-1>t&&(x+=d+c[l+1].length)}if(!b){o.idx[d]=t[n],o.interLft2[d]=R,o.interLft1[d]=L,o.interRgt2[d]=A,o.interRgt1[d]=S,o.chars[d]=z,o.terms[d]=E,o.interIns[d]=C,o.intraIns[d]=y,o.start[d]=m;let e=h.match(a),l=e.index+e[1].length,r=k.length,i=r>0?0:1/0,s=r-4;for(let t=2;e.length>t;)if(i>s||k[i]!=l)l+=e[t].length,t++;else{let n=k[i+1],r=k[i+2],s=k[i+3],a=t,g="";for(let t=0;n>t;a++)g+=e[a],t+=e[a].length;e.splice(t,a-t,g),l+=_(e,t,r,s),i+=4}l=e.index+e[1].length;let g=o.ranges[d]=[],u=l,f=l;for(let t=2;e.length>t;t++){let n=e[t].length;l+=n,t%2==0?f=l:n>0&&(g.push(u,f),u=f=l)}f>u&&g.push(u,f),d++}}if(t.length>d)for(let e in o)o[e]=o[e].slice(0,d);return o},_=(e,t,l,n)=>{let r=e[t]+e[t+1].slice(0,l);return e[t-1]+=r,e[t]=e[t+1].slice(l,l+n),e[t+1]=e[t+1].slice(l+n),r.length};return{search:(...t)=>((t,n,i,s=1e3,a)=>{i=i?!0===i?5:i:0;let g=null,u=null,f=[];n=n.replace(k,(e=>{let t=e.trim().slice(1);return t='"'===t[0]?l(t.slice(1,-1)):t.replace(r,""),""!=t&&f.push(t),""}));let c,o=D(n);if(f.length>0){if(c=RegExp(f.join("|"),"i"+I),0==o.length){let e=[];for(let l=0;t.length>l;l++)c.test(t[l])||e.push(l);return[e,null,null]}}else if(0==o.length)return[null,null,null];if(i>0){let e=D(n);if(e.length>1){let l=e.slice().sort(((e,t)=>t.length-e.length));for(let e=0;l.length>e;e++){if(0==a?.length)return[[],null,null];a=O(t,l[e],a)}if(e.length>i)return[a,null,null];g=h(e).map((e=>e.join(" "))),u=[];let n=new Set;for(let e=0;g.length>e;e++)if(a.length>n.size){let l=a.filter((e=>!n.has(e))),r=O(t,g[e],l);for(let e=0;r.length>e;e++)n.add(r[e]);u.push(r)}else u.push([])}}null==g&&(g=[n],u=[a?.length>0?a:O(t,n)]);let p=null,d=null;if(f.length>0&&(u=u.map((e=>e.filter((e=>!c.test(t[e])))))),s>=u.reduce(((e,t)=>e+t.length),0)){p={},d=[];for(let l=0;u.length>l;l++){let n=u[l];if(null==n||0==n.length)continue;let r=g[l],i=P(n,t,r),s=e.sort(i,t,r);if(l>0)for(let e=0;s.length>e;e++)s[e]+=d.length;for(let e in i)p[e]=(p[e]??[]).concat(i[e]);d=d.concat(s)}}return[[].concat(...u),p,d]})(...t),split:D,filter:O,info:P,sort:e.sort}}const f=(()=>{let e={A:"ÁÀÃÂÄĄ",a:"áàãâäą",E:"ÉÈÊËĖ",e:"éèêëę",I:"ÍÌÎÏĮ",i:"íìîïį",O:"ÓÒÔÕÖ",o:"óòôõö",U:"ÚÙÛÜŪŲ",u:"úùûüūų",C:"ÇČĆ",c:"çčć",L:"Ł",l:"ł",N:"ÑŃ",n:"ñń",S:"ŠŚ",s:"šś",Z:"ŻŹ",z:"żź"},t=new Map,l="";for(let n in e)e[n].split("").forEach((e=>{l+=e,t.set(e,n)}));let n=RegExp(`[${l}]`,"g"),r=e=>t.get(e);return e=>{if("string"==typeof e)return e.replace(n,r);let t=Array(e.length);for(let l=0;e.length>l;l++)t[l]=e[l].replace(n,r);return t}})();function h(e){let t,l,n=(e=e.slice()).length,r=[e.slice()],i=Array(n).fill(0),s=1;for(;n>s;)s>i[s]?(t=s%2&&i[s],l=e[s],e[s]=e[t],e[t]=l,++i[s],s=1,r.push(e.slice())):(i[s]=0,++s);return r}const c=(e,t)=>t?`${e}`:e,o=(e,t)=>e+t;return u.latinize=f,u.permute=e=>h([...Array(e.length).keys()]).sort(((e,t)=>{for(let l=0;e.length>l;l++)if(e[l]!=t[l])return e[l]-t[l];return 0})).map((t=>t.map((t=>e[t])))),u.highlight=function(e,t,l=c,n="",r=o){n=r(n,l(e.substring(0,t[0]),!1))??n;for(let i=0;t.length>i;i+=2)n=r(n,l(e.substring(t[i],t[i+1]),!0))??n,t.length-3>i&&(n=r(n,l(e.substring(t[i+1],t[i+2]),!1))??n);return r(n,l(e.substring(t[t.length-1]),!1))??n},u}();
3 |
--------------------------------------------------------------------------------
/src/public/mode-yarn.js:
--------------------------------------------------------------------------------
1 | define('ace/mode/yarn', [
2 | 'require',
3 | 'exports',
4 | 'module',
5 | 'ace/lib/oop',
6 | 'ace/mode/text',
7 | 'ace/mode/text_highlight_rules',
8 | 'ace/mode/behaviour',
9 | ], function(require, exports, module) {
10 | 'use strict';
11 |
12 | var oop = require('../lib/oop');
13 | var TextMode = require('./text').Mode;
14 | var TextHighlightRules = require('./text_highlight_rules').TextHighlightRules;
15 | var CstyleBehaviour = require('./behaviour/cstyle').CstyleBehaviour;
16 |
17 | var YarnHighlightRules = function() {
18 | this.$rules = {
19 | start: [
20 | {
21 | token: 'comment',
22 | regex: '^\\/\\/.*$',
23 | },
24 | {
25 | token: 'paren.lcomm',
26 | regex: '<<',
27 | next: 'comm',
28 | },
29 | {
30 | token: 'paren.llink',
31 | regex: '\\[\\[',
32 | next: 'link',
33 | },
34 | ],
35 | link: [
36 | {
37 | token: 'string.rlink',
38 | regex: '\\|\\w*[a-zA-Z0-9 ]+',
39 | },
40 | {
41 | token: 'string.llink',
42 | regex: '[a-zA-Z0-9 ]+',
43 | },
44 | {
45 | token: 'paren.rlink',
46 | regex: '\\]\\]',
47 | next: 'start',
48 | },
49 | ],
50 | comm: [
51 | {
52 | token: 'string.comm',
53 | regex: "[A-Za-z0-9 _.,!:''/$ ]+",
54 | },
55 | {
56 | token: 'paren.rcomm',
57 | regex: '>>',
58 | next: 'start',
59 | },
60 | ],
61 | };
62 | };
63 |
64 | var Mode = function() {
65 | this.HighlightRules = YarnHighlightRules;
66 | this.$behaviour = new CstyleBehaviour();
67 | };
68 |
69 | oop.inherits(YarnHighlightRules, TextHighlightRules);
70 | oop.inherits(Mode, TextMode);
71 |
72 | (function() {
73 | this.type = 'text';
74 | this.getNextLineIndent = function(state, line, tab) {
75 | return this.$getIndent(line); //automatic indentation of new line. Return '' to disable it
76 | };
77 | this.$id = 'ace/mode/yarn';
78 | }.call(Mode.prototype));
79 |
80 | exports.Mode = Mode;
81 |
82 | /// set context menu
83 | $.contextMenu(app.utils.getEditorContextMenu(/\|/g));
84 |
85 | /// Enable autocompletion via word guessing
86 | app.editor.setOptions({
87 | enableBasicAutocompletion: app.settings.autocompleteSuggestionsEnabled(),
88 | enableLiveAutocompletion: app.settings.autocompleteSuggestionsEnabled(),
89 | behavioursEnabled: app.settings.autoCloseBrackets(),
90 | });
91 | });
92 |
--------------------------------------------------------------------------------
/src/public/plugins/ace-diff/ace-diff-dark.min.css:
--------------------------------------------------------------------------------
1 | .acediff__wrap{display:flex;flex-direction:row;position:absolute;bottom:0;top:0;left:0;height:100%;width:100%;overflow:auto}.acediff__gutter{flex:0 0 60px;border-left:1px solid #000;border-right:1px solid #000;overflow:hidden}.acediff__gutter,.acediff__gutter svg{background-color:#272727}.acediff__left,.acediff__right{height:100%;flex:1}.acediff__diffLine{background-color:#004d7a;border-top:1px solid #003554;border-bottom:1px solid #003554;position:absolute;z-index:4}.acediff__diffLine.targetOnly{height:0!important;border-top:1px solid #003554;border-bottom:0;position:absolute}.acediff__connector{fill:#004d7a;stroke:#003554}.acediff__copy--left,.acediff__copy--right{position:relative}.acediff__copy--left div,.acediff__copy--right div{color:#fff;text-shadow:1px 1px rgba(0,0,0,.7);position:absolute;margin:2px 3px;cursor:pointer}.acediff__copy--right div:hover{color:#61a2e7}.acediff__copy--left{float:right}.acediff__copy--left div{right:0}.acediff__copy--left div:hover{color:#f7b742}
2 | /*# sourceMappingURL=/ace-diff-dark.min.css.map */
--------------------------------------------------------------------------------
/src/public/plugins/ace-diff/ace-diff.min.css:
--------------------------------------------------------------------------------
1 | /*! Ace-diff | github.com/ace-diff/ace-diff */
2 | .acediff__wrap{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;position:absolute;bottom:0;top:0;left:0;height:100%;width:100%;overflow:auto}.acediff__gutter{-webkit-box-flex:0;-ms-flex:0 0 60px;flex:0 0 60px;border-left:1px solid #bcbcbc;border-right:1px solid #bcbcbc;overflow:hidden}.acediff__gutter,.acediff__gutter svg{background-color:#efefef}.acediff__left,.acediff__right{height:100%;-webkit-box-flex:1;-ms-flex:1;flex:1}.acediff__diffLine{background-color:#d8f2ff;border-top:1px solid #a2d7f2;border-bottom:1px solid #a2d7f2;position:absolute;z-index:4}.acediff__diffLine.targetOnly{height:0!important;border-top:1px solid #a2d7f2;border-bottom:0;position:absolute}.acediff__connector{fill:#d8f2ff;stroke:#a2d7f2}.acediff__copy--left,.acediff__copy--right{position:relative}.acediff__copy--left div,.acediff__copy--right div{color:#000;text-shadow:1px 1px hsla(0,0%,100%,.7);position:absolute;margin:2px 3px;cursor:pointer}.acediff__copy--right div:hover{color:#004ea0}.acediff__copy--left{float:right}.acediff__copy--left div{right:0}.acediff__copy--left div:hover{color:#c98100}
--------------------------------------------------------------------------------
/src/public/plugins/inkjs/ink-renderer.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';// todo remove this emitter
2 | const { Story } = inkjs;
3 |
4 | export var inkRender = function() {
5 | let emiter = new EventEmitter(); //todo add signals
6 | this.emiter = emiter;
7 | this.story = null;
8 | this.log = [];
9 | this.onRecompile = () => {};
10 |
11 | this.curStory = {
12 | messages: [],
13 | choices: [],
14 | tags: [],
15 | paragraphEl: '',
16 | };
17 | this.resetStory = () => {
18 | this.prevSavePoints = [];
19 | this.choiceHistory = [];
20 | this.textAreaEl.innerHTML = '';
21 | this.curStory = {
22 | messages: [],
23 | choices: [],
24 | tags: [],
25 | paragraphEl: '',
26 | };
27 | this.story.ResetState();
28 | };
29 | this.terminate = () => {
30 | if (!this.textAreaEl) return;
31 | try {
32 | emiter.removeAllListeners();
33 | this.finished = true;
34 | } catch (e) {
35 | console.warn(e);
36 | }
37 | };
38 |
39 | this.setCurStory = ({ messages, choices, tags, paragraphEl }) => {
40 | this.curStory = { messages, choices, tags, paragraphEl };
41 | };
42 | const getMessage = _story => {
43 | let message = [];
44 | while (_story.canContinue) {
45 | message.push(_story.Continue().replace(/\n/g, ''));
46 | }
47 | return message;
48 | };
49 |
50 | const continueStory = (choiceLabel = '', choicePath = '') => {
51 | const paragraph = getMessage(this.story);
52 | const gotoFindQuery = choiceLabel.includes('"')
53 | ? choiceLabel
54 | : `[${choiceLabel}]`;
55 |
56 | const paragraphEl = document.createElement('p');
57 | if (choiceLabel) {
58 | const paragraphElementTitle = document.createElement('p');
59 | paragraphElementTitle.innerHTML = `${choiceLabel} ( ${choicePath} )
`;
60 | paragraphElementTitle.onclick = () =>
61 | app.navigateToNodeDuringPlayTest(choicePath, gotoFindQuery);
62 |
63 | paragraphEl.appendChild(paragraphElementTitle);
64 | paragraph.forEach(p => {
65 | const message = document.createElement('p');
66 | message.innerHTML = `${p}
`;
67 | paragraphEl.appendChild(message);
68 | });
69 | } else {
70 | paragraphEl.innerHTML = paragraph.join('
');
71 | }
72 |
73 | this.setCurStory({
74 | ...this.curStory,
75 | messages: this.log
76 | ? [
77 | ...this.curStory.messages,
78 | choiceLabel ? `--${choiceLabel}--` : '',
79 | paragraph,
80 | ].filter(Boolean)
81 | : [paragraph],
82 | tags: this.story.currentTags,
83 | choices: this.story.currentChoices,
84 | paragraphEl,
85 | });
86 |
87 | updateText();
88 | };
89 |
90 | this.prevSavePoints = [];
91 | const getChoice = (index, label) => {
92 | this.prevSavePoints.push(this.story.state.toJson());
93 | const choicePath = this.story.state.currentChoices[index].sourcePath.split(
94 | '.'
95 | )[0];
96 | this.story.ChooseChoiceIndex(index);
97 | continueStory(label, choicePath);
98 | };
99 | this.rewindStory = () => {
100 | document.getElementById('choiceButtons').remove();
101 | this.textAreaEl.removeChild(this.textAreaEl.lastElementChild);
102 | this.story.state.LoadJson(this.prevSavePoints.pop());
103 | continueStory();
104 | };
105 |
106 | this.createAndAddParagraph = child => {
107 | console.log('made', child);
108 | if (child.innerHTML) {
109 | const paragraph = document.createElement('p');
110 | paragraph.appendChild(child);
111 | paragraph.className =
112 | 'story-playtest-bubble story-playtest-answer answer-post fade-in is-paused';
113 | this.textAreaEl.appendChild(paragraph);
114 | $(paragraph).removeClass('is-paused');
115 | }
116 | };
117 | // html update stuff
118 | const updateText = () => {
119 | this.createAndAddParagraph(this.curStory.paragraphEl);
120 | this.textAreaEl.querySelectorAll('div').forEach(n => n.remove());
121 | const btnWrapper = document.createElement('div');
122 | btnWrapper.id = 'choiceButtons';
123 | btnWrapper.className = 'flex-wrap';
124 | // Debug tools
125 | const reloadBtn = document.createElement('button');
126 | reloadBtn.innerText = '🔄';
127 | reloadBtn.title = 'Recompile story';
128 | reloadBtn.onclick = this.onRecompile;
129 | reloadBtn.className = 'storyPreviewChoiceButton';
130 | btnWrapper.appendChild(reloadBtn);
131 | const restartBtn = document.createElement('button');
132 | restartBtn.innerText = '🎬'; //'🔄';
133 | restartBtn.title = 'Restart story';
134 | restartBtn.onclick = () => {
135 | this.resetStory();
136 | continueStory();
137 | };
138 | restartBtn.className = 'storyPreviewChoiceButton';
139 | btnWrapper.appendChild(restartBtn);
140 | const rewindBtn = document.createElement('button');
141 | rewindBtn.innerText = '⏪';
142 | rewindBtn.title = 'Go to previous';
143 | rewindBtn.disabled = this.prevSavePoints.length === 0;
144 | rewindBtn.onclick = () => {
145 | this.rewindStory();
146 | continueStory();
147 | };
148 | btnWrapper.appendChild(rewindBtn);
149 | rewindBtn.className = 'storyPreviewChoiceButton';
150 | // choices
151 | this.curStory.choices.forEach((choice, index) => {
152 | const btn = document.createElement('button');
153 | btn.innerText = choice.text;
154 | btn.onclick = e => {
155 | e.stopPropagation();
156 | getChoice(index, choice.text);
157 | };
158 | btn.className = 'storyPreviewChoiceButton';
159 | btnWrapper.appendChild(btn);
160 | });
161 | this.textAreaEl.appendChild(btnWrapper);
162 |
163 | this.textAreaEl.scrollTo({
164 | top: this.textAreaEl.scrollHeight + 100,
165 | left: 0,
166 | behavior: 'smooth',
167 | });
168 | };
169 |
170 | this.initInk = (
171 | compiler,
172 | onRecompile,
173 | prevSession,
174 | inkTextData,
175 | startChapter,
176 | htmlIdToAttachTo,
177 | resourcesPath,
178 | debugLabelId,
179 | playtestStyle,
180 | playtestVariables //TODO
181 | ) => {
182 | this.onRecompile = onRecompile;
183 | console.log('INIT INK');
184 | this.finished = false;
185 | document.getElementById(htmlIdToAttachTo).style.visibility = 'hidden';
186 | this.textAreaEl = document.getElementById(debugLabelId);
187 | this.textAreaEl.innerHTML =
188 | '';
189 |
190 | this.inkTextData = inkTextData;
191 | this.compiler = compiler;
192 |
193 | this.compiler
194 | .init(response => {
195 | this.textAreaEl.innerHTML = '';
196 | if (response.errors.length > 0) {
197 | this.textAreaEl.innerHTML = `Parsing failed:
>
${response.errors.join(
198 | '
'
199 | )}
${response.warnings.join('
')}
`;
200 | this.textAreaEl.onclick = () => {
201 | console.log('====>', response);
202 | app.data.goToErrorInkNode(this.inkTextData, response.errors[0]);
203 | this.textAreaEl.onclick = null;
204 | };
205 | return;
206 | }
207 | if (response.warnings.length > 0) {
208 | const warningsEl = document.createElement('p');
209 | warningsEl.className = 'title-warning';
210 | response.warnings.forEach(warning => {
211 | const warningEl = document.createElement('p');
212 | warningEl.innerText = warning;
213 | warningEl.onclick = () => {
214 | app.data.goToErrorInkNode(this.inkTextData, warning);
215 | };
216 | warningsEl.appendChild(warningEl);
217 | });
218 | this.createAndAddParagraph(warningsEl);
219 | }
220 | this.story = new Story(response.story);
221 | console.log('STORY', this.story);
222 | console.warn('Warnings', response.warnings);
223 | continueStory();
224 |
225 | //Try to restart story from specific chapter when there is no start chapter specified in the global scope
226 | if (
227 | this.story.currentText === '' &&
228 | this.story.currentChoices.length === 0
229 | ) {
230 | if (startChapter !== app.data.InkGlobalScopeNodeName) {
231 | this.compiler.submit(`-> ${startChapter}\n` + inkTextData);
232 | } else {
233 | const firstFoundNode = inkTextData
234 | .split('\n')
235 | .find(line => line.includes('==='));
236 | this.compiler.submit(
237 | `-> ${firstFoundNode.split('===')[1]}\n` + inkTextData
238 | );
239 | }
240 | }
241 | })
242 | .then(() => {
243 | if (
244 | !prevSession.recompile &&
245 | prevSession.story &&
246 | prevSession.prevSavePoints.length !== 0
247 | ) {
248 | this.story = prevSession.story;
249 | prevSession.childNodes.forEach(child =>
250 | this.textAreaEl.appendChild(child)
251 | );
252 | this.prevSavePoints = prevSession.prevSavePoints;
253 | continueStory();
254 | return;
255 | }
256 | if (inkTextData) this.compiler.submit(inkTextData);
257 | });
258 | };
259 | };
260 |
--------------------------------------------------------------------------------
/src/public/plugins/jsoneditor/jsoneditor.js:
--------------------------------------------------------------------------------
1 | export var JSONEditor = function({ id }) {
2 | this.tableAdd = ({ key, value }) => {
3 | var $clone = this.table
4 | .find('tr.hide')
5 | .clone(true)
6 | .removeClass('hide table-line');
7 | if (key && value) {
8 | console.log('mutate', { key, value, html: $clone.html() });
9 | $clone.html(`
10 | ${key} |
11 |
${value} |
12 |
13 | x delete
14 | |
15 | `);
16 | $('.table-remove').on('click', function() {
17 | $(this)
18 | .parents('tr')
19 | .detach();
20 | });
21 | }
22 | this.table.find('table').append($clone);
23 | };
24 | this.getValue = () => {
25 | var $rows = this.table.find('tr:not(:hidden)');
26 | var headers = [];
27 | var data = [];
28 |
29 | // Get the headers (add special header logic here)
30 | $($rows.shift())
31 | .find('th:not(:empty):not([data-attr-ignore])')
32 | .each(function() {
33 | headers.push(
34 | $(this)
35 | .text()
36 | .toLowerCase()
37 | );
38 | });
39 |
40 | // Turn all existing rows into a loopable array
41 | $rows.each(function() {
42 | var $td = $(this).find('td');
43 | var h = {};
44 | // Use the headers from earlier to name our hash keys
45 | headers.forEach(function(header, i) {
46 | h[header] = $td.eq(i).text(); // will adapt for inputs if text is empty
47 | });
48 |
49 | data.push(h);
50 | });
51 | // Output the result
52 | // var $EXPORT = $('#save');
53 | // $EXPORT.text(JSON.stringify(data));
54 | return data;
55 | };
56 |
57 | this.init = () => {
58 | document.getElementById(id).innerHTML = `
59 |
60 |
61 |
62 |
67 |
68 |
69 |
70 |
71 | key |
72 | val |
73 |
74 | x delete
75 | |
76 |
77 |
78 |
79 |
84 |
85 |
86 | `;
87 | var $TABLE = $('#table');
88 | var $BTN = $('#save-btn');
89 | this.table = $TABLE;
90 | this.rows = $TABLE.find('tr:not(:hidden)');
91 | var $EXPORT = $('#save-btn');
92 | $EXPORT.addClass('hide');
93 |
94 | $('.table-add').on('click', this.tableAdd);
95 |
96 | $('.table-remove').on('click', function() {
97 | console.log({ removeThis: this });
98 | $(this)
99 | .parents('tr')
100 | .detach();
101 | });
102 |
103 | // A few jQuery helpers for exporting only
104 | jQuery.fn.pop = [].pop;
105 | jQuery.fn.shift = [].shift;
106 | $BTN.on('click', this.getValue);
107 | };
108 | this.init();
109 |
110 | this.setValue = (newValue = []) => {
111 | newValue.forEach(this.tableAdd);
112 | };
113 | };
114 |
--------------------------------------------------------------------------------
/src/public/plugins/jsoneditor/size-overrides.css:
--------------------------------------------------------------------------------
1 | @media only screen and (max-width: 600px) {
2 | .swal2-popup{
3 | padding: 2px !important;
4 | }
5 | .swal2-actions{
6 | margin: 1px !important;
7 | }
8 | }
9 |
10 | .swal2-title{
11 | width:100%;
12 | }
13 | .swal2-content{
14 | max-height: 90vh;
15 | overflow: auto;
16 | }
17 | @media only screen and (max-width: 600px) {
18 | /*.form-control > input {*/
19 | /* max-width: 100px;*/
20 | /*}*/
21 | }
22 |
23 | .table {
24 | width: 100%;
25 | }
26 | .table-editable {
27 | width: 100%;
28 | position: relative;
29 | }
30 |
31 | .table-header {
32 | position: sticky;
33 | top: 0;
34 | background-color: #007;
35 | color: aqua;
36 | }
37 |
38 | .table-footer {
39 | position: sticky;
40 | bottom: 0;
41 | background-color: #007;
42 | color: aqua;
43 | display: flex;
44 | justify-content: space-between;
45 | }
46 | .table-editable .glyphicon {
47 | font-size: 20px;
48 | }
49 |
50 | .table-remove {
51 | color: #700;
52 | cursor: pointer;
53 | text-align: center;
54 | }
55 |
56 | .cell {
57 | width: 180px;
58 | max-width: 180px;
59 | text-align: left;
60 | padding-left: 3px;
61 | padding-right: 3px;
62 | }
63 | .table-remove:hover {
64 | color: #f00;
65 | }
66 |
67 | .table-up:hover,
68 | .table-down:hover {
69 | color: #00f;
70 | }
71 |
72 | .table-add {
73 | width: 120px;
74 | color: #070;
75 | cursor: pointer;
76 | text-align: center;
77 | }
78 |
79 | .table-add:hover {
80 | color: #0b0;
81 | }
82 |
83 | .hide {
84 | display: none;
85 | }
--------------------------------------------------------------------------------
/src/public/plugins/runner.js:
--------------------------------------------------------------------------------
1 | import { yarnRender } from './bondage/renderer';
2 | import { inkRender } from './inkjs/ink-renderer';
3 | const { JSONEditor } = require('./jsoneditor/jsoneditor');
4 |
5 | export var Runner = function({
6 | app,
7 | createButton,
8 | addSettingsItem,
9 | getPluginStore,
10 | onYarnEditorOpen,
11 | onYarnInPreviewMode,
12 | onYarnSavedNode,
13 | onYarnSetDocumentType,
14 | onKeyUp,
15 | onKeyDown,
16 | onLoad,
17 | setPluginStore,
18 | }) {
19 | const self = this;
20 | this.name = 'Runner';
21 |
22 | // Variables editor dialog
23 | this.onOpenDialog = async () => {
24 | let editor = null;
25 | const { value: formValues } = await Swal.fire({
26 | title: 'Playtest starting variables',
27 | html: '
',
28 | focusConfirm: false,
29 | customClass: 'swal-wide',
30 | onOpen: () => {
31 | // create the editor
32 | require('./jsoneditor/size-overrides.css');
33 | editor = new JSONEditor({ id: 'jsoneditor' });
34 | const localVariables = getPluginStore(self.name);
35 | app.log({ editor });
36 | // set json
37 | editor.setValue(
38 | typeof localVariables.variables !== 'object'
39 | ? [{ key: 'er', value: 'erd' }]
40 | : localVariables.variables
41 | );
42 | setPluginStore(self.name, 'runnerVariablesOpen', true);
43 | },
44 | preConfirm: () => {
45 | setPluginStore(self.name, 'runnerVariablesOpen', false);
46 | return editor.getValue();
47 | },
48 | });
49 |
50 | if (formValues) {
51 | setPluginStore(self.name, 'variables', formValues);
52 | }
53 | };
54 |
55 | onLoad(() => {
56 | // add actions
57 | addSettingsItem({
58 | title: 'Playtesting Style',
59 | valueKey: 'playtestStyle',
60 | defaultValue: 'chat',
61 | optionsKey: 'availablePlaytestStyles',
62 | options: [
63 | { id: 'npc', name: 'Npc bubble' },
64 | { id: 'chat', name: 'Chat messages' },
65 | ],
66 | setterKey: 'setPlaytestStyle',
67 | settingsColumn: 'A',
68 | });
69 | if(app.settings.developmentModeEnabled()) {
70 | // create a button in the file menu
71 | createButton(self.name, {
72 | name: 'Playtest variables',
73 | className: 'item',
74 | attachTo: 'fileMenuDropdown',
75 | onClick: 'onOpenDialog()',
76 | iconName: 'cog',
77 | });
78 | };
79 |
80 | const localVariables = getPluginStore(self.name);
81 | if (localVariables.runnerVariablesOpen) self.onOpenDialog();
82 | });
83 |
84 | const updateRunnerMode = () => {
85 | // YARN PLAY MODE
86 | if (app.settings.documentType() === 'yarn') {
87 | this.previewStory = new yarnRender();
88 |
89 | this.gotoLastPlayNode = function() {
90 | if (
91 | app.editing() &&
92 | app.editing().title() !== self.previewStory.node.title
93 | ) {
94 | app.openNodeByTitle(self.previewStory.node.title);
95 | }
96 | app.editor.focus();
97 | };
98 |
99 | this.advanceStoryPlayMode = function(speed = 5) {
100 | if (!self.previewStory.finished) {
101 | self.previewStory.changeTextScrollSpeed(speed);
102 | if (self.previewStory.vnSelectedChoice != -1 && speed === 5) {
103 | self.previewStory.vnSelectChoice();
104 | }
105 | } else {
106 | self.togglePlayMode(false);
107 | self.gotoLastPlayNode();
108 | }
109 | };
110 |
111 | this.togglePlayMode = function(playModeOverwrite = false) {
112 | const editor = $('.editor')[0];
113 | const storyPreviewPlayButton = document.getElementById(
114 | 'storyPlayButton'
115 | );
116 | const editorPlayPreviewer = document.getElementById('editor-play');
117 | $('#editor-play').addClass('inYarnMode');
118 | $('#commandDebugLabel').addClass('inYarnMode');
119 | app.isEditorInPlayMode(playModeOverwrite);
120 | if (playModeOverwrite) {
121 | //preview play mode
122 | editor.style.display = 'none';
123 | $('.bbcode-toolbar').addClass('hidden');
124 | editorPlayPreviewer.style.display = 'flex';
125 | $(storyPreviewPlayButton).addClass('disabled');
126 | self.previewStory.emiter.on('finished', function() {
127 | self.togglePlayMode(false);
128 | self.gotoLastPlayNode();
129 | });
130 | self.previewStory.emiter.on('startedNode', function(e) {
131 | if (app.isEditorSplit) {
132 | app.workspace.warpToNode(
133 | app.getFirstFoundNode(e.title.toLowerCase().trim())
134 | );
135 | }
136 | });
137 | const localVariables = getPluginStore(self.name);
138 | app.log('variables', localVariables);
139 | app.data.getSaveData('json').then(saveData => {
140 | self.previewStory.initYarn(
141 | JSON.parse(saveData),
142 | app
143 | .editing()
144 | .title()
145 | .trim(),
146 | 'NVrichTextLabel',
147 | false,
148 | 'commandDebugLabel',
149 | app.settings.playtestStyle(),
150 | localVariables.variables || []
151 | );
152 | });
153 | } else {
154 | //edit mode
155 | app.editor.session.setScrollTop(editorPlayPreviewer.scrollTop);
156 | editorPlayPreviewer.style.display = 'none';
157 | editor.style.display = 'flex';
158 | $(storyPreviewPlayButton).removeClass('disabled');
159 | $('.bbcode-toolbar').removeClass('hidden');
160 | $('.toggle-toolbar').removeClass('hidden');
161 | $('.editor-counter').removeClass('hidden');
162 | self.previewStory.terminate();
163 | }
164 | };
165 |
166 | onYarnInPreviewMode(() => self.togglePlayMode(false));
167 | onYarnSavedNode(() => self.togglePlayMode(false));
168 |
169 | onYarnEditorOpen(() => {
170 | createButton(self.name, {
171 | iconName: 'play',
172 | title: 'Preview',
173 | attachTo: 'bbcodeToolbar',
174 | onClick: 'togglePlayMode(true)',
175 | className: 'bbcode-button bbcode-button-right',
176 | id: 'storyPlayButton',
177 | });
178 |
179 | const element = document.createElement('div');
180 | element.innerHTML = `
181 |
185 | `;
186 | document.getElementById('editorContainer').appendChild(element);
187 |
188 | onKeyDown(e => {
189 | if (!app.editing() || self.previewStory.finished) return;
190 | switch (e.keyCode) {
191 | case app.input.keys.Z:
192 | self.previewStory.changeTextScrollSpeed(10);
193 | if (self.previewStory.vnSelectedChoice != -1)
194 | self.previewStory.vnSelectChoice();
195 | break;
196 |
197 | case app.input.keys.Up:
198 | if (self.previewStory.vnSelectedChoice != -1)
199 | self.previewStory.vnUpdateChoice(-1);
200 | break;
201 |
202 | case app.input.keys.Down:
203 | if (self.previewStory.vnSelectedChoice != -1)
204 | self.previewStory.vnUpdateChoice(1);
205 | break;
206 | }
207 | });
208 | onKeyUp(e => {
209 | if (e.keyCode === app.input.keys.Z) {
210 | self.previewStory.changeTextScrollSpeed(200);
211 | if (self.previewStory.vnSelectedChoice != -1)
212 | self.previewStory.vnSelectChoice();
213 | }
214 | });
215 | });
216 | } else {
217 | // INKJS PLAY MODE //
218 | this.previewStory = new inkRender();
219 | this.prevSession = {
220 | story: null,
221 | prevSavePoints: [],
222 | childNodes: [],
223 | recompile: false,
224 | };
225 | const compiler = new app.data.InkCompiler(); ///
226 | this.togglePlayMode = function(playModeOverwrite = false) {
227 | const editor = $('.editor')[0];
228 | const storyPreviewPlayButton = document.getElementById(
229 | 'storyPlayButton'
230 | );
231 | const editorPlayPreviewer = document.getElementById('editor-play');
232 | app.isEditorInPlayMode(playModeOverwrite);
233 | $('#editor-play').addClass('inInkMode');
234 | $('#commandDebugLabel').addClass('inInkMode');
235 |
236 | if (playModeOverwrite) {
237 | //preview play mode
238 | editorPlayPreviewer.style.display = 'flex';
239 | $('#editor').addClass('editor-take-half');
240 |
241 | self.previewStory.emiter.on('finished', function() {
242 | self.togglePlayMode(false);
243 | self.gotoLastPlayNode();
244 | });
245 | self.previewStory.emiter.on('startedNode', function(e) {
246 | if (app.isEditorSplit) {
247 | app.workspace.warpToNode(
248 | app.getFirstFoundNode(e.title.toLowerCase().trim())
249 | );
250 | }
251 | });
252 | const localVariables = getPluginStore(self.name);
253 | app.log('VARIABLES::::', localVariables);
254 |
255 | app.data.getSaveData('ink', null, true).then(saveData => {
256 | const onRecompile = () => {
257 | self.prevSession = { ...self.prevSession, recompile: true };
258 | self.togglePlayMode(true);
259 | };
260 | self.previewStory.initInk(
261 | compiler,
262 | onRecompile,
263 | self.prevSession,
264 | saveData,
265 | app
266 | .editing()
267 | .title()
268 | .trim(),
269 | 'NVrichTextLabel',
270 | false,
271 | 'commandDebugLabel',
272 | app.settings.playtestStyle(),
273 | localVariables.variables || []
274 | );
275 | });
276 | } else {
277 | //edit mode
278 | app.editor.session.setScrollTop(editorPlayPreviewer.scrollTop);
279 | editorPlayPreviewer.style.display = 'none';
280 | editor.style.display = 'flex';
281 | $('#editor').removeClass('editor-take-half');
282 | $(storyPreviewPlayButton).removeClass('disabled');
283 |
284 | self.prevSession = {
285 | prevSavePoints: self.previewStory.prevSavePoints,
286 | story: self.previewStory.story,
287 | childNodes: self.previewStory.textAreaEl
288 | ? [...self.previewStory.textAreaEl.childNodes]
289 | : [],
290 | recompile: false,
291 | };
292 | self.previewStory.terminate();
293 | }
294 | app.editor.resize();
295 | };
296 | onYarnInPreviewMode(() => self.togglePlayMode(false));
297 | onYarnSavedNode(() => self.togglePlayMode(false));
298 | this.advanceStoryPlayMode = function(speed = 5) {};
299 |
300 | onYarnEditorOpen(() => {
301 | createButton(self.name, {
302 | iconName: 'play',
303 | title: 'Preview',
304 | attachTo: 'bbcodeToolbar',
305 | onClick: 'togglePlayMode(!app.isEditorInPlayMode())',
306 | className: 'bbcode-button bbcode-button-right',
307 | id: 'storyPlayButton',
308 | });
309 |
310 | const element = document.createElement('div');
311 | element.innerHTML = `
312 |
316 | `;
317 | document.getElementById('editorContainer').appendChild(element);
318 | });
319 | }
320 | };
321 | updateRunnerMode();
322 | //yarnSetDocumentType
323 | onYarnSetDocumentType(updateRunnerMode);
324 | //TODO remove this ugly hack
325 | app.togglePlayMode = this.togglePlayMode;
326 | };
327 |
--------------------------------------------------------------------------------
/src/public/templates/node.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/public/theme-ink.js:
--------------------------------------------------------------------------------
1 | define('ace/theme/ink', [
2 | 'require',
3 | 'exports',
4 | 'module',
5 | 'ace/lib/dom',
6 | ], function(require, exports, module) {
7 | exports.isDark = false;
8 | exports.cssClass = 'ace-ink';
9 | exports.cssText = `
10 | /* Flow related stuff in blue */
11 | .ace_editor span.ace_flow.ace_declaration,
12 | .ace_editor span.ace_divert,
13 | .ace_editor span.ace_choice.ace_bullets,
14 | .ace_editor span.ace_choice.ace_label,
15 | .ace_editor span.ace_choice.ace_weaveBracket,
16 | .ace_editor span.ace_gather.ace_bullets,
17 | .ace_editor span.ace_gather.ace_label,
18 | .ace_editor span.ace_glue,
19 | .ace_editor span.ace_include,
20 | .ace_editor span.ace_external {
21 | color: blue;
22 | }
23 |
24 | /* Comments */
25 | .ace-tm .ace_comment {
26 | color: #84756c;
27 | }
28 |
29 | /* Logic */
30 | .ace_editor span.ace_var-decl,
31 | .ace_editor span.ace_list-decl,
32 | .ace_editor span.ace_logic:not(.ace_innerContent) {
33 | color: green;
34 | }
35 |
36 | /* Tags */
37 | .ace_editor span.ace_tag {
38 | color: #AAA;
39 | }
40 |
41 | #main.hideTags .ace_editor span.ace_tag {
42 | color: white;
43 | }
44 |
45 | /* Custom added */
46 | .ace_editor .ace_marker-layer .ace_selection {
47 | background: rgb(181, 213, 255);
48 | }
49 | .ace_editor.ace_multiselect .ace_selection.ace_start {
50 | box-shadow: 0 0 3px 0px white;
51 | border-radius: 2px;
52 | }
53 | .ace_editor .ace_marker-layer .ace_selection {
54 | background: rgb(181, 213, 255);
55 | }
56 | .ace_editor.ace_multiselect .ace_selection.ace_start {
57 | box-shadow: 0 0 3px 0px white;
58 | border-radius: 2px;
59 | }
60 | `;
61 |
62 | var dom = require('../lib/dom');
63 | dom.importCssString(exports.cssText, exports.cssClass);
64 | });
65 |
--------------------------------------------------------------------------------
/src/public/theme-yarn.js:
--------------------------------------------------------------------------------
1 | define("ace/theme/yarn",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
2 |
3 | exports.isDark = false;
4 | exports.cssClass = "ace-yarn";
5 | exports.cssText = "\
6 | .ace-yarn .ace_gutter {\
7 | background: #e8e8e8;\
8 | color: #AAA;\
9 | }\
10 | .ace-yarn {\
11 | background: #fff;\
12 | color: #000;\
13 | }\
14 | .ace-yarn .ace_comment {\
15 | color: #00c171;\
16 | font-style: italic;\
17 | }\
18 | .ace-yarn .ace_variable.ace_language {\
19 | color: #0086B3;\
20 | }\
21 | .ace-yarn .ace_paren {\
22 | font-weight: bold;\
23 | }\
24 | .ace-yarn .ace_string.ace_llink {\
25 | color: #000;\
26 | }\
27 | .ace-yarn .ace_string.ace_rlink {\
28 | color: #3ecfe9;\
29 | }\
30 | .ace-yarn .ace_string.ace_comm {\
31 | color: #e93ecf;\
32 | }\
33 | .ace-yarn .ace_paren.ace_lcomm, .ace-yarn .ace_paren.ace_rcomm {\
34 | color: #e00ec0;\
35 | }\
36 | .ace-yarn .ace_paren.ace_llink, .ace-yarn .ace_paren.ace_rlink {\
37 | color: #0ec0e0;\
38 | }\
39 | .ace-yarn .ace_variable.ace_instance {\
40 | color: teal;\
41 | }\
42 | .ace-yarn .ace_constant.ace_language {\
43 | font-weight: bold;\
44 | }\
45 | .ace-yarn .ace_cursor {\
46 | color: black;\
47 | }\
48 | .ace-yarn .ace_marker-layer .ace_active-line {\
49 | background: rgb(255, 255, 204);\
50 | }\
51 | .ace-yarn .ace_marker-layer .ace_selection {\
52 | background: rgb(181, 213, 255);\
53 | }\
54 | .ace-yarn.ace_multiselect .ace_selection.ace_start {\
55 | box-shadow: 0 0 3px 0px white;\
56 | border-radius: 2px;\
57 | }\
58 | .ace-yarn.ace_nobold .ace_line > span {\
59 | font-weight: normal !important;\
60 | }\
61 | .ace-yarn .ace_marker-layer .ace_step {\
62 | background: rgb(252, 255, 0);\
63 | }\
64 | .ace-yarn .ace_marker-layer .ace_stack {\
65 | background: rgb(164, 229, 101);\
66 | }\
67 | .ace-yarn .ace_marker-layer .ace_bracket {\
68 | margin: -1px 0 0 -1px;\
69 | border: 1px solid rgb(192, 192, 192);\
70 | }\
71 | .ace-yarn .ace_gutter-active-line {\
72 | background-color : rgba(0, 0, 0, 0.07);\
73 | }\
74 | .ace-yarn .ace_marker-layer .ace_selected-word {\
75 | background: rgb(250, 250, 255);\
76 | border: 1px solid rgb(200, 200, 250);\
77 | }\
78 | .ace-yarn .ace_print-margin {\
79 | width: 1px;\
80 | background: #e8e8e8;\
81 | }\
82 | .ace-yarn .ace_indent-guide {\
83 | background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
84 | }";
85 |
86 | var dom = require("../lib/dom");
87 | dom.importCssString(exports.cssText, exports.cssClass);
88 | });
--------------------------------------------------------------------------------
/src/public/themes/blueprint.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --text-color: #2f919a;
3 | }
4 | /*-- Scrollbar ------------------------------------------------------------------ */
5 |
6 | ::-webkit-scrollbar-thumb:hover {
7 | background: orange;
8 | border: 1px solid orange;
9 | }
10 |
11 |
12 | /*-- Menu ------------------------------------------------------------------ */
13 | .app-menu {
14 | position: absolute;
15 | top: 0;
16 | left: 0;
17 | width: 100%;
18 | height: 30px;
19 | background-color: white;
20 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
21 | }
22 |
23 | .app-menu .menu {
24 | display: block;
25 | float: left;
26 | margin-left: 10px;
27 | border-radius: 2px;
28 | box-shadow: 0;
29 | font-size: 0.9em;
30 | color: #666;
31 | cursor: pointer;
32 | }
33 |
34 | .title {
35 | box-sizing: border-box;
36 | padding: 5px;
37 | width: fit-content;
38 | min-width: 100px;
39 | line-height: 20px;
40 | font-weight: bold;
41 | float: left;
42 | }
43 |
44 | .menu .dropdown {
45 | color: gray;
46 | background-color: white;
47 | border-bottom-left-radius: 12px;
48 | border-bottom-right-radius: 12px;
49 | box-shadow: 0 5px 5px rgba(0, 0, 0, 0.4);
50 | }
51 |
52 | .menu .dropdown .item:hover {
53 | background: orange;
54 | color: black;
55 | }
56 |
57 | .menu .dropdown .item:active {
58 | background: orange;
59 | color: black;
60 | }
61 |
62 | .menu:hover .title {
63 | background: orange;
64 | color: black;
65 | }
66 |
67 | .menu:active .title {
68 | background: orange;
69 | color: #555;
70 | }
71 |
72 | .menu-icon {
73 | color: orange;
74 | }
75 |
76 | .menu .dropdown .item:hover .menu-icon {
77 | color: black;
78 | }
79 |
80 | .add-link {
81 | box-shadow: 0 0 1px 1px #e8e8e8;
82 | }
83 |
84 | .add-link:hover {
85 | background: orange;
86 | }
87 |
88 | #linkHelperMenuFilter {
89 | border-right: 2px solid orange;
90 | border-left: 2px solid orange;
91 | }
92 |
93 | /*-- Search ------------------------------------------------------------------ */
94 | .app-search {
95 | position: absolute;
96 | top: 0;
97 | right: 10px;
98 | }
99 |
100 | .app-search input {
101 | font-family: 'Lucida Console', Monaco, monospace;
102 | }
103 |
104 | .app-search input[type='checkbox'] {
105 | cursor: pointer;
106 | }
107 |
108 | .app-search .search-field {
109 | border-top: 0;
110 | border-left: 0;
111 | border-right: 0;
112 | border-bottom: 1px solid darkgray;
113 | }
114 |
115 | .app-search .dropdown .item {
116 | display: block;
117 | box-sizing: border-box;
118 | padding: 7px;
119 | }
120 |
121 | /*-- ----------------------------------------------------------------------- */
122 |
123 | #app-bg {
124 | /* .nodes-holder { */
125 | width:5000px;
126 | height:5000px;
127 | background-color: CornflowerBlue;
128 | background-size: 50px 50px, 50px 50px, 10px 10px, 10px 10px;
129 | }
130 |
131 | #marquee {
132 | display: none;
133 | border: 1px double white;
134 | background-color: white;
135 | background-color: rgba(255, 255, 255, .1);
136 | }
137 |
138 | .node-editor .form {
139 | top: 2.5%;
140 | }
141 |
142 | .bbcode-button:hover {
143 | background: orange;
144 | }
145 |
146 | .node {
147 | border-top-left-radius: 15px;
148 | border-top-right-radius: 15px;
149 | border-bottom-left-radius: 12px;
150 | border-bottom-right-radius: 12px;
151 | box-shadow: 0 10px 10px rgba(0, 0, 0, 0.4);
152 | }
153 |
154 | .node.selected {
155 | border: inherit;
156 | background-color: orange;
157 | color: black;
158 | }
159 | .node .body {
160 | padding: 0 5px 0 5px;
161 | }
162 | .node.selected .body {
163 | background-color: orange;
164 | color: black;
165 | }
166 | .node .title {
167 | width: 100%;
168 | height: 40px;
169 | line-height: 40px;
170 | border-top-left-radius: 12px;
171 | border-top-right-radius: 12px;
172 | }
173 | .node .tags {
174 | padding: 0;
175 | margin: 0;
176 | width: 100%;
177 | border: 0;
178 | border-bottom-left-radius: 12px;
179 | border-bottom-right-radius: 12px;
180 | background: inherit;
181 | }
182 | .node .tags:empty {
183 | display:none;
184 | }
185 | .node .tags span {
186 | padding: 1px 5px 1px 5px;
187 | margin: 0 0 0 2px;
188 | border-radius: 15px;
189 | background-color: #E0D6C5;
190 | color: #8D8374;
191 | }
192 |
193 | /* SPECIAL CHECKBOX START */
194 | .styled-checkbox input:checked + label {
195 | background-color: #FBCEB1;
196 | border: 2px solid orange;
197 | }
198 |
199 | .checked-button {
200 | background-color: #EFC7C7;
201 | }
202 | .styled-checkbox input:checked + label.transcribe-button {
203 | background-color: #EFC7C7;
204 | border: 2px solid #DC8484;
205 | }
206 |
207 | .settings-icon {
208 | color: orange;
209 | }
210 |
211 | .tag-style-1 {
212 | border: 1px solid red;
213 | background-color: red !important;
214 | color: yellow !important;
215 | }
216 | .tag-style-2 {
217 | border: 1px solid #6EA5E0 !important;
218 | background-color: #6EA5E0 !important;
219 | color: black !important;
220 | }
221 | .tag-style-3 {
222 | background-color: #9EDE74 !important;
223 | }
224 | .tag-style-4 {
225 | background-color: #FFE374 !important;
226 | }
227 | .tag-style-5 {
228 | background-color: #F7A666 !important;
229 | }
230 | .tag-style-6 {
231 | background-color: #C47862 !important;
232 | }
233 | .tag-style-7 {
234 | background-color: #97E1E9 !important;
235 | }
236 | .tag-style-8 {
237 | background-color: #576574 !important;
238 | color: white;
239 | }
240 | .tag-style-9 {
241 | background-color: black !important;
242 | color: white;
243 | }
244 | .title-style-1 {
245 | background-color: white;
246 | color: black;
247 | }
248 | .title-style-2 {
249 | background-color: #FF6B6B;
250 | color: white;
251 | }
252 | .title-style-3 {
253 | background-color: #1CD1A1;
254 | color: white;
255 | }
256 | .title-style-4 {
257 | background-color: #2E86DE;
258 | color: white;
259 | }
260 | .title-style-5 {
261 | background-color: #FECA57;
262 | color: white;
263 | }
264 | .title-style-6 {
265 | background-color: #5F27CD;
266 | color: white;
267 | }
268 | .title-style-7 {
269 | background-color: #D15519;
270 | color: white;
271 | }
272 | .title-style-8 {
273 | background-color: #576574;
274 | color: white;
275 | }
276 | .title-style-9 {
277 | background-color: black;
278 | color: white;
279 | }
280 |
281 | .app-info {
282 | color: #153d58;
283 | }
284 | /*-- Canvas Buttons ------------------------------------------------------- */
285 | .app-add-node {
286 | color: grey;
287 | }
288 |
289 | .app-add-node:hover {
290 | color: orange;
291 | }
292 |
293 | .app-button span {
294 | color: grey;
295 | }
296 |
297 | .app-button span:hover {
298 | color: orange;
299 | }
300 |
301 | /* --------------------------- Narrow overrides -----------------------------*/
302 | @media only screen and (max-width: 600px) {
303 | .app-menu {
304 | box-shadow: 0 0;
305 | }
306 |
307 | .app-search {
308 | position: absolute;
309 | top: 26px;
310 | left: 0px;
311 | width: 100%;
312 | background-color: white;
313 | padding: 5px 10px 5px 5px;
314 | z-index: 10000;
315 | }
316 |
317 | .app-search > input {
318 | padding: unset;
319 | width: 170px;
320 | }
321 | }
322 |
323 | .grid-canvas {
324 | color: lightgrey;
325 | }
326 |
327 | .arrows {
328 | color: white;
329 | }
330 |
331 | .button,
332 | button {
333 | background: orange;
334 | }
--------------------------------------------------------------------------------
/src/public/themes/classic.css:
--------------------------------------------------------------------------------
1 | /*-- General ------------------------------------------------------------------ */
2 | :root {
3 | --yarnBg: #baf5f2;
4 | --yarnFg: #00dcff;
5 | --text-color: #134347;
6 | }
7 |
8 | #app-bg {
9 | background: -webkit-linear-gradient(45deg, #ddc9b9 0%, #daf0f2 100%);
10 | }
11 |
12 | a {
13 | color: #2f919a;
14 | text-decoration: none;
15 | transition: color 0.25s;
16 | }
17 |
18 | a:hover {
19 | color: #000;
20 | }
21 | .styled-checkbox {
22 | background-color: white;
23 | }
24 | .styled-checkbox>label>svg,
25 | .bbcode-button>svg {
26 | fill: #8a8a8a;
27 | }
28 |
29 | .styled-checkbox:active>label>svg,
30 | .styled-checkbox:hover>label>svg,
31 | .bbcode-button:hover >svg {
32 | fill: #09292b;
33 | }
34 |
35 | /*-- Menu ------------------------------------------------------------------ */
36 |
37 | .app-menu {
38 | display: flex;
39 | position: absolute;
40 | top: 0;
41 | left: 0;
42 | width: 100%;
43 | height: 30px;
44 | background-color: transparent;
45 | }
46 |
47 | .app-menu .menu {
48 | display: block;
49 | float: left;
50 | margin-left: 10px;
51 | border-radius: 2px;
52 | box-shadow: 0;
53 | font-size: 0.9em;
54 | color: #666;
55 | cursor: pointer;
56 | background-color: white;
57 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4);
58 | margin: 5px;
59 | }
60 |
61 | .title {
62 | box-sizing: border-box;
63 | padding: 5px;
64 | width: fit-content;
65 | min-width: 100px;
66 | line-height: 20px;
67 | font-weight: bold;
68 | float: left;
69 | }
70 |
71 | .menu .dropdown {
72 | color: #2e8074;
73 | background-color: white;
74 | box-shadow: 0 5px 5px rgba(0, 0, 0, 0.4);
75 | }
76 |
77 | .menu .dropdown .item:hover .menu-icon {
78 | color: black;
79 | }
80 |
81 | .menu-icon {
82 | color: #289aa5;
83 | }
84 |
85 | .settings-icon {
86 | color: #289aa5;
87 | }
88 |
89 | /*-- Canvas Buttons ------------------------------------------------------- */
90 | .app-add-node {
91 | color: grey;
92 | }
93 |
94 | .app-add-node:hover {
95 | color: var(--yarnFg);
96 | }
97 |
98 | .app-button span {
99 | color: grey;
100 | }
101 |
102 | .app-button span:hover {
103 | color: var(--yarnFg);
104 | }
105 |
106 | .app-add-node, .app-button {
107 | opacity: 0.7;
108 | }
109 |
110 | /*-- Search ----------------------------------------------------------------- */
111 |
112 | .app-search {
113 | position: absolute;
114 | top: 3px;
115 | right: 10px;
116 | }
117 |
118 | .app-search input {
119 | font-family: 'Lucida Console', Monaco, monospace;
120 | margin-left: 1px;
121 | margin-right: 1px;
122 | }
123 |
124 | .app-search .search-field {
125 | border-radius: 15px;
126 | border-width: thin;
127 | border-color: cyan;
128 | margin-left: 0px;
129 | margin-right: 0px;
130 | }
131 | .search-field:focus {
132 | background-color: var(--yarnBg);
133 | }
134 |
135 | .dropdown .item:hover {
136 | background-color: var(--yarnBg);
137 | color: black;
138 | }
139 |
140 | .app-search input[type='checkbox'] {
141 | cursor: pointer;
142 | }
143 |
144 | .app-search .dropdown .item {
145 | display: block;
146 | box-sizing: border-box;
147 | padding: 7px;
148 | }
149 |
150 | #openHelperMenu > span {
151 | color: rgb(121 121 121);
152 | }
153 | /*-- Node ------------------------------------------------------------------ */
154 |
155 | .node.selected {
156 | box-shadow: 0 0 0 2px #49eff1;
157 | }
158 |
159 | .node:hover {
160 | box-shadow: 0 0 0 2px #49eff1;
161 | }
162 |
163 | .title-style-1 {
164 | background-color: #EBEBEB;
165 | color: black;
166 | }
167 | .title-style-2 {
168 | background-color: #6EA5E0;
169 | }
170 | .title-style-3 {
171 | background-color: #9EDE74;
172 | }
173 | .title-style-4 {
174 | background-color: #FFE374;
175 | }
176 |
177 | .title-style-5 {
178 | background-color: #F7A666;
179 | }
180 | .title-style-6 {
181 | background-color: #C47862;
182 | }
183 | .title-style-7 {
184 | background-color: #97E1E9;
185 | }
186 | .title-style-8 {
187 | background-color: #576574;
188 | color: white;
189 | }
190 | .title-style-9 {
191 | background-color: black;
192 | color: white;
193 | }
194 |
195 | .node.selected {
196 | box-shadow: 0px 0px 0px 2px cyan;
197 | border: none;
198 | }
199 |
200 | .app-info {
201 | color: #b9786d;
202 | }
203 | /*-- ----------------------------Narrow overrides---------------------------- */
204 |
205 | @media only screen and (max-width: 600px) {
206 | .app-menu {
207 | box-shadow: 0 0;
208 | }
209 |
210 | .app-search {
211 | top: 34px;
212 | left: 0px;
213 | position: absolute;
214 | width: 50%;
215 | padding: 5px 10px 5px 5px;
216 | z-index: 10000;
217 | }
218 |
219 | .app-search input {
220 | padding: unset;
221 | width: 170px;
222 | }
223 | }
224 |
225 | .grid-canvas {
226 | color: grey;
227 | }
228 |
229 | .button,
230 | button {
231 | background: #99ebe4;
232 | }
233 |
234 | .content-data-image {
235 | content: "url(" attr(data-src) ") ";
236 | }
--------------------------------------------------------------------------------
/src/public/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.4.381"
3 | }
--------------------------------------------------------------------------------
/src/scss/font/context-menu-icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/scss/font/context-menu-icons.eot
--------------------------------------------------------------------------------
/src/scss/font/context-menu-icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/scss/font/context-menu-icons.ttf
--------------------------------------------------------------------------------
/src/scss/font/context-menu-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/scss/font/context-menu-icons.woff
--------------------------------------------------------------------------------
/src/scss/font/context-menu-icons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blurymind/YarnClassic/ce8052b977da6dcb02936c6d52d9edcb2003bc3c/src/scss/font/context-menu-icons.woff2
--------------------------------------------------------------------------------
/src/scss/jquery.contextMenu.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*!
3 | * jQuery contextMenu - Plugin for simple contextMenu handling
4 | *
5 | * Version: v2.7.0
6 | *
7 | * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
8 | * Web: http://swisnl.github.io/jQuery-contextMenu/
9 | *
10 | * Copyright (c) 2011-2018 SWIS BV and contributors
11 | *
12 | * Licensed under
13 | * MIT License http://www.opensource.org/licenses/mit-license
14 | *
15 | * Date: 2018-10-02T14:29:27.829Z
16 | */
17 | @-webkit-keyframes cm-spin {
18 | 0% {
19 | -webkit-transform: translateY(-50%) rotate(0deg);
20 | transform: translateY(-50%) rotate(0deg);
21 | }
22 | 100% {
23 | -webkit-transform: translateY(-50%) rotate(359deg);
24 | transform: translateY(-50%) rotate(359deg);
25 | }
26 | }
27 | @-o-keyframes cm-spin {
28 | 0% {
29 | -webkit-transform: translateY(-50%) rotate(0deg);
30 | -o-transform: translateY(-50%) rotate(0deg);
31 | transform: translateY(-50%) rotate(0deg);
32 | }
33 | 100% {
34 | -webkit-transform: translateY(-50%) rotate(359deg);
35 | -o-transform: translateY(-50%) rotate(359deg);
36 | transform: translateY(-50%) rotate(359deg);
37 | }
38 | }
39 | @keyframes cm-spin {
40 | 0% {
41 | -webkit-transform: translateY(-50%) rotate(0deg);
42 | -o-transform: translateY(-50%) rotate(0deg);
43 | transform: translateY(-50%) rotate(0deg);
44 | }
45 | 100% {
46 | -webkit-transform: translateY(-50%) rotate(359deg);
47 | -o-transform: translateY(-50%) rotate(359deg);
48 | transform: translateY(-50%) rotate(359deg);
49 | }
50 | }
51 |
52 | @font-face {
53 | font-family: "context-menu-icons";
54 | font-style: normal;
55 | font-weight: normal;
56 |
57 | src: url("font/context-menu-icons.eot?2gb3e");
58 | src: url("font/context-menu-icons.eot?2gb3e#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2gb3e") format("woff2"), url("font/context-menu-icons.woff?2gb3e") format("woff"), url("font/context-menu-icons.ttf?2gb3e") format("truetype");
59 | }
60 |
61 | .context-menu-icon-add:before {
62 | content: "\EA01";
63 | }
64 |
65 | .context-menu-icon-copy:before {
66 | content: "\EA02";
67 | }
68 |
69 | .context-menu-icon-cut:before {
70 | content: "\EA03";
71 | }
72 |
73 | .context-menu-icon-delete:before {
74 | content: "\EA04";
75 | }
76 |
77 | .context-menu-icon-edit:before {
78 | content: "\EA05";
79 | }
80 |
81 | .context-menu-icon-loading:before {
82 | content: "\EA06";
83 | }
84 |
85 | .context-menu-icon-paste:before {
86 | content: "\EA07";
87 | }
88 |
89 | .context-menu-icon-quit:before {
90 | content: "\EA08";
91 | }
92 |
93 | .context-menu-icon::before {
94 | position: absolute;
95 | top: 50%;
96 | left: 0;
97 | width: 2em;
98 | font-family: "context-menu-icons";
99 | font-size: 1em;
100 | font-style: normal;
101 | font-weight: normal;
102 | line-height: 1;
103 | color: #2980b9;
104 | text-align: center;
105 | -webkit-transform: translateY(-50%);
106 | -ms-transform: translateY(-50%);
107 | -o-transform: translateY(-50%);
108 | transform: translateY(-50%);
109 |
110 | -webkit-font-smoothing: antialiased;
111 | -moz-osx-font-smoothing: grayscale;
112 | }
113 |
114 | .context-menu-icon.context-menu-hover:before {
115 | color: #fff;
116 | }
117 |
118 | .context-menu-icon.context-menu-disabled::before {
119 | color: #bbb;
120 | }
121 |
122 | .context-menu-icon.context-menu-icon-loading:before {
123 | -webkit-animation: cm-spin 2s infinite;
124 | -o-animation: cm-spin 2s infinite;
125 | animation: cm-spin 2s infinite;
126 | }
127 |
128 | .context-menu-icon.context-menu-icon--fa {
129 | display: list-item;
130 | font-family: inherit;
131 | line-height: inherit;
132 | }
133 | .context-menu-icon.context-menu-icon--fa::before {
134 | position: absolute;
135 | top: 50%;
136 | left: 0;
137 | width: 2em;
138 | font-family: FontAwesome;
139 | font-size: 1em;
140 | font-style: normal;
141 | font-weight: normal;
142 | line-height: 1;
143 | color: #2980b9;
144 | text-align: center;
145 | -webkit-transform: translateY(-50%);
146 | -ms-transform: translateY(-50%);
147 | -o-transform: translateY(-50%);
148 | transform: translateY(-50%);
149 |
150 | -webkit-font-smoothing: antialiased;
151 | -moz-osx-font-smoothing: grayscale;
152 | }
153 | .context-menu-icon.context-menu-icon--fa.context-menu-hover:before {
154 | color: #fff;
155 | }
156 | .context-menu-icon.context-menu-icon--fa.context-menu-disabled::before {
157 | color: #bbb;
158 | }
159 |
160 | .context-menu-icon.context-menu-icon--fa5 {
161 | display: list-item;
162 | font-family: inherit;
163 | line-height: inherit;
164 | }
165 | .context-menu-icon.context-menu-icon--fa5 i, .context-menu-icon.context-menu-icon--fa5 svg {
166 | position: absolute;
167 | top: .3em;
168 | left: .5em;
169 | color: #2980b9;
170 | }
171 | .context-menu-icon.context-menu-icon--fa5.context-menu-hover > i, .context-menu-icon.context-menu-icon--fa5.context-menu-hover > svg {
172 | color: #fff;
173 | }
174 | .context-menu-icon.context-menu-icon--fa5.context-menu-disabled i, .context-menu-icon.context-menu-icon--fa5.context-menu-disabled svg {
175 | color: #bbb;
176 | }
177 |
178 | .context-menu-list {
179 | position: absolute;
180 | display: inline-block;
181 | min-width: 13em;
182 | max-width: 26em;
183 | padding: .25em 0;
184 | margin: .3em;
185 | font-family: inherit;
186 | font-size: inherit;
187 | list-style-type: none;
188 | background: #fff;
189 | border: 1px solid #bebebe;
190 | border-radius: .2em;
191 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
192 | box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
193 | }
194 |
195 | .context-menu-item {
196 | position: relative;
197 | -webkit-box-sizing: content-box;
198 | -moz-box-sizing: content-box;
199 | box-sizing: content-box;
200 | padding: .2em 2em;
201 | color: #2f2f2f;
202 | -webkit-user-select: none;
203 | -moz-user-select: none;
204 | -ms-user-select: none;
205 | user-select: none;
206 | background-color: #fff;
207 | }
208 |
209 | .context-menu-separator {
210 | padding: 0;
211 | margin: .35em 0;
212 | border-bottom: 1px solid #e6e6e6;
213 | }
214 |
215 | .context-menu-item > label > input,
216 | .context-menu-item > label > textarea {
217 | -webkit-user-select: text;
218 | -moz-user-select: text;
219 | -ms-user-select: text;
220 | user-select: text;
221 | }
222 |
223 | .context-menu-item.context-menu-hover {
224 | color: #fff;
225 | cursor: pointer;
226 | background-color: #2980b9;
227 | }
228 |
229 | .context-menu-item.context-menu-disabled {
230 | color: #bbb;
231 | cursor: default;
232 | background-color: #fff;
233 | }
234 |
235 | .context-menu-input.context-menu-hover {
236 | color: #2f2f2f;
237 | cursor: default;
238 | }
239 |
240 | .context-menu-submenu:after {
241 | position: absolute;
242 | top: 50%;
243 | right: .5em;
244 | z-index: 1;
245 | width: 0;
246 | height: 0;
247 | content: '';
248 | border-color: transparent transparent transparent #2f2f2f;
249 | border-style: solid;
250 | border-width: .25em 0 .25em .25em;
251 | -webkit-transform: translateY(-50%);
252 | -ms-transform: translateY(-50%);
253 | -o-transform: translateY(-50%);
254 | transform: translateY(-50%);
255 | }
256 |
257 | /**
258 | * Inputs
259 | */
260 | .context-menu-item.context-menu-input {
261 | padding: .3em .6em;
262 | }
263 |
264 | /* vertically align inside labels */
265 | .context-menu-input > label > * {
266 | vertical-align: top;
267 | }
268 |
269 | /* position checkboxes and radios as icons */
270 | .context-menu-input > label > input[type="checkbox"],
271 | .context-menu-input > label > input[type="radio"] {
272 | position: relative;
273 | top: .12em;
274 | margin-right: .4em;
275 | }
276 |
277 | .context-menu-input > label {
278 | margin: 0;
279 | }
280 |
281 | .context-menu-input > label,
282 | .context-menu-input > label > input[type="text"],
283 | .context-menu-input > label > textarea,
284 | .context-menu-input > label > select {
285 | display: block;
286 | width: 100%;
287 | -webkit-box-sizing: border-box;
288 | -moz-box-sizing: border-box;
289 | box-sizing: border-box;
290 | }
291 |
292 | .context-menu-input > label > textarea {
293 | height: 7em;
294 | }
295 |
296 | .context-menu-item > .context-menu-list {
297 | top: .3em;
298 | /* re-positioned by js */
299 | right: -.3em;
300 | display: none;
301 | }
302 |
303 | .context-menu-item.context-menu-visible > .context-menu-list {
304 | display: block;
305 | }
306 |
307 | .context-menu-accesskey {
308 | text-decoration: underline;
309 | }
310 |
--------------------------------------------------------------------------------
/src/scss/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /**
8 | * Correct `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | main,
20 | nav,
21 | section,
22 | summary {
23 | display: block;
24 | }
25 |
26 | /**
27 | * Correct `inline-block` display not defined in IE 8/9.
28 | */
29 |
30 | audio,
31 | canvas,
32 | video {
33 | display: inline-block;
34 | }
35 |
36 | /**
37 | * Prevent modern browsers from displaying `audio` without controls.
38 | * Remove excess height in iOS 5 devices.
39 | */
40 |
41 | audio:not([controls]) {
42 | display: none;
43 | height: 0;
44 | }
45 |
46 | /**
47 | * Address `[hidden]` styling not present in IE 8/9.
48 | * Hide the `template` element in IE, Safari, and Firefox < 22.
49 | */
50 |
51 | [hidden],
52 | template {
53 | display: none;
54 | }
55 |
56 | /* ==========================================================================
57 | Base
58 | ========================================================================== */
59 |
60 | /**
61 | * 1. Set default font family to sans-serif.
62 | * 2. Prevent iOS text size adjust after orientation change, without disabling
63 | * user zoom.
64 | */
65 |
66 | html {
67 | font-family: sans-serif; /* 1 */
68 | -ms-text-size-adjust: 100%; /* 2 */
69 | -webkit-text-size-adjust: 100%; /* 2 */
70 | }
71 |
72 | /**
73 | * Remove default margin.
74 | */
75 |
76 | body {
77 | margin: 0;
78 | }
79 |
80 | /* ==========================================================================
81 | Links
82 | ========================================================================== */
83 |
84 | /**
85 | * Remove the gray background color from active links in IE 10.
86 | */
87 |
88 | a {
89 | background: transparent;
90 | }
91 |
92 | /**
93 | * Address `outline` inconsistency between Chrome and other browsers.
94 | */
95 |
96 | a:focus {
97 | outline: thin dotted;
98 | }
99 |
100 | /**
101 | * Improve readability when focused and also mouse hovered in all browsers.
102 | */
103 |
104 | a:active,
105 | a:hover {
106 | outline: 0;
107 | }
108 |
109 | /* ==========================================================================
110 | Typography
111 | ========================================================================== */
112 |
113 | /**
114 | * Address variable `h1` font-size and margin within `section` and `article`
115 | * contexts in Firefox 4+, Safari 5, and Chrome.
116 | */
117 |
118 | h1 {
119 | font-size: 2em;
120 | margin: 0.67em 0;
121 | }
122 |
123 | /**
124 | * Address styling not present in IE 8/9, Safari 5, and Chrome.
125 | */
126 |
127 | abbr[title] {
128 | border-bottom: 1px dotted;
129 | }
130 |
131 | /**
132 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: bold;
138 | }
139 |
140 | /**
141 | * Address styling not present in Safari 5 and Chrome.
142 | */
143 |
144 | dfn {
145 | font-style: italic;
146 | }
147 |
148 | /**
149 | * Address differences between Firefox and other browsers.
150 | */
151 |
152 | hr {
153 | -moz-box-sizing: content-box;
154 | box-sizing: content-box;
155 | height: 0;
156 | }
157 |
158 | /**
159 | * Address styling not present in IE 8/9.
160 | */
161 |
162 | mark {
163 | background: #ff0;
164 | color: #000;
165 | }
166 |
167 | /**
168 | * Correct font family set oddly in Safari 5 and Chrome.
169 | */
170 |
171 | code,
172 | kbd,
173 | pre,
174 | samp {
175 | font-family: monospace, serif;
176 | font-size: 1em;
177 | }
178 |
179 | /**
180 | * Improve readability of pre-formatted text in all browsers.
181 | */
182 |
183 | pre {
184 | white-space: pre-wrap;
185 | }
186 |
187 | /**
188 | * Set consistent quote types.
189 | */
190 |
191 | q {
192 | quotes: "\201C" "\201D" "\2018" "\2019";
193 | }
194 |
195 | /**
196 | * Address inconsistent and variable font size in all browsers.
197 | */
198 |
199 | small {
200 | font-size: 80%;
201 | }
202 |
203 | /**
204 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
205 | */
206 |
207 | sub,
208 | sup {
209 | font-size: 75%;
210 | line-height: 0;
211 | position: relative;
212 | vertical-align: baseline;
213 | }
214 |
215 | sup {
216 | top: -0.5em;
217 | }
218 |
219 | sub {
220 | bottom: -0.25em;
221 | }
222 |
223 | /* ==========================================================================
224 | Embedded content
225 | ========================================================================== */
226 |
227 | /**
228 | * Remove border when inside `a` element in IE 8/9.
229 | */
230 |
231 | img {
232 | border: 0;
233 | }
234 |
235 | /**
236 | * Correct overflow displayed oddly in IE 9.
237 | */
238 |
239 | svg:not(:root) {
240 | overflow: hidden;
241 | }
242 |
243 | /* ==========================================================================
244 | Figures
245 | ========================================================================== */
246 |
247 | /**
248 | * Address margin not present in IE 8/9 and Safari 5.
249 | */
250 |
251 | figure {
252 | margin: 0;
253 | }
254 |
255 | /* ==========================================================================
256 | Forms
257 | ========================================================================== */
258 |
259 | /**
260 | * Define consistent border, margin, and padding.
261 | */
262 |
263 | fieldset {
264 | border: 1px solid #c0c0c0;
265 | margin: 0 2px;
266 | padding: 0.35em 0.625em 0.75em;
267 | }
268 |
269 | /**
270 | * 1. Correct `color` not being inherited in IE 8/9.
271 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
272 | */
273 |
274 | legend {
275 | border: 0; /* 1 */
276 | padding: 0; /* 2 */
277 | }
278 |
279 | /**
280 | * 1. Correct font family not being inherited in all browsers.
281 | * 2. Correct font size not being inherited in all browsers.
282 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
283 | */
284 |
285 | button,
286 | input,
287 | select,
288 | textarea {
289 | font-family: inherit; /* 1 */
290 | font-size: 100%; /* 2 */
291 | margin: 0; /* 3 */
292 | }
293 |
294 | /**
295 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
296 | * the UA stylesheet.
297 | */
298 |
299 | button,
300 | input {
301 | line-height: normal;
302 | }
303 |
304 | /**
305 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
306 | * All other form control elements do not inherit `text-transform` values.
307 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
308 | * Correct `select` style inheritance in Firefox 4+ and Opera.
309 | */
310 |
311 | button,
312 | select {
313 | text-transform: none;
314 | }
315 |
316 | /**
317 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
318 | * and `video` controls.
319 | * 2. Correct inability to style clickable `input` types in iOS.
320 | * 3. Improve usability and consistency of cursor style between image-type
321 | * `input` and others.
322 | */
323 |
324 | button,
325 | html input[type="button"], /* 1 */
326 | input[type="reset"],
327 | input[type="submit"] {
328 | -webkit-appearance: button; /* 2 */
329 | cursor: pointer; /* 3 */
330 | }
331 |
332 | /**
333 | * Re-set default cursor for disabled elements.
334 | */
335 |
336 | button[disabled],
337 | html input[disabled] {
338 | cursor: default;
339 | }
340 |
341 | /**
342 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
343 | * 2. Remove excess padding in IE 8/9/10.
344 | */
345 |
346 | input[type="checkbox"],
347 | input[type="radio"] {
348 | box-sizing: border-box; /* 1 */
349 | padding: 0; /* 2 */
350 | }
351 |
352 | /**
353 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
354 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
355 | * (include `-moz` to future-proof).
356 | */
357 |
358 | input[type="search"] {
359 | -webkit-appearance: textfield; /* 1 */
360 | -moz-box-sizing: content-box;
361 | -webkit-box-sizing: content-box; /* 2 */
362 | box-sizing: content-box;
363 | }
364 |
365 | /**
366 | * Remove inner padding and search cancel button in Safari 5 and Chrome
367 | * on OS X.
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Remove inner padding and border in Firefox 4+.
377 | */
378 |
379 | button::-moz-focus-inner,
380 | input::-moz-focus-inner {
381 | border: 0;
382 | padding: 0;
383 | }
384 |
385 | /**
386 | * 1. Remove default vertical scrollbar in IE 8/9.
387 | * 2. Improve readability and alignment in all browsers.
388 | */
389 |
390 | textarea {
391 | overflow: auto; /* 1 */
392 | vertical-align: top; /* 2 */
393 | }
394 |
395 | /* ==========================================================================
396 | Tables
397 | ========================================================================== */
398 |
399 | /**
400 | * Remove most spacing between table cells.
401 | */
402 |
403 | table {
404 | border-collapse: collapse;
405 | border-spacing: 0;
406 | }
407 |
--------------------------------------------------------------------------------
/src/sw-src.js:
--------------------------------------------------------------------------------
1 | import { precacheAndRoute } from 'workbox-precaching';
2 | import { NetworkFirst } from 'workbox-strategies';
3 | import { registerRoute } from 'workbox-routing';
4 |
5 | console.log("Yarn's service worker is caching files");
6 | registerRoute(/\.\/YarnClassic\//, new NetworkFirst());
7 | precacheAndRoute(self.__WB_MANIFEST);
8 |
--------------------------------------------------------------------------------
/testFiles/Sally.yarn.txt:
--------------------------------------------------------------------------------
1 | title: Sally
2 | tags:
3 | colorID: 0
4 | position: 524,111
5 | ---
6 | <
>
7 | Player: Hey, Sally.
8 | Sally: Oh! Hi.
9 | Sally: You snuck up on me.
10 | Sally: Don't do that.
11 | <>
12 | Player: Hey.
13 | Sally: Hi.
14 | <>
15 |
16 | <>
17 | [[Anything exciting happen on your watch?|Sally.Watch]]
18 | <>
19 |
20 | <>
21 | [[Sorry about the console.|Sally.Sorry]]
22 | <>
23 | [[See you later.|Sally.Exit]]
24 | ===
25 | title: Sally.Watch
26 | tags:
27 | colorID: 0
28 | position: 516,538
29 | ---
30 | Sally: Not really.
31 | Sally: Same old nebula, doing the same old thing.
32 | Sally: Oh, Ship wanted to see you. Go say hi to it.
33 | <>
34 | <>
35 | Player: Already done!
36 | Sally: Go say hi again.
37 | <>
38 | ===
39 | title: Sally.Exit
40 | tags:
41 | colorID: 6
42 | position: 145,384
43 | ---
44 | Sally: Bye.
45 | ===
46 | title: Sally.Sorry
47 | tags:
48 | colorID: 0
49 | position: 873,444
50 | ---
51 | Sally: Yeah. Don't do it again.
52 | ===
53 |
--------------------------------------------------------------------------------
/testFiles/assignment.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Numeric",
4 | "tags": "Tag",
5 | "body": "Test Line\n<>\nTest Line After",
6 | "position": {
7 | "x": 449,
8 | "y": 252
9 | },
10 | "colorID": 0
11 | },{
12 | "title": "NumericExpression",
13 | "tags": "Tag",
14 | "body": "Test Line\n<>\nTest Line After",
15 | "position": {
16 | "x": 449,
17 | "y": 252
18 | },
19 | "colorID": 0
20 | },
21 | {
22 | "title": "String",
23 | "tags": "Tag",
24 | "body": "Test Line\n<>\nTest Line After",
25 | "position": {
26 | "x": 449,
27 | "y": 252
28 | },
29 | "colorID": 0
30 | },
31 | {
32 | "title": "StringExpression",
33 | "tags": "Tag",
34 | "body": "Test Line\n<>\nTest Line After",
35 | "position": {
36 | "x": 449,
37 | "y": 252
38 | },
39 | "colorID": 0
40 | },
41 | {
42 | "title": "Boolean",
43 | "tags": "Tag",
44 | "body": "Test Line\n<>\nTest Line After",
45 | "position": {
46 | "x": 449,
47 | "y": 252
48 | },
49 | "colorID": 0
50 | },
51 | {
52 | "title": "BooleanExpression",
53 | "tags": "Tag",
54 | "body": "Test Line\n<>\nTest Line After",
55 | "position": {
56 | "x": 449,
57 | "y": 252
58 | },
59 | "colorID": 0
60 | },
61 | {
62 | "title": "Null",
63 | "tags": "Tag",
64 | "body": "Test Line\n<>\nTest Line After",
65 | "position": {
66 | "x": 449,
67 | "y": 252
68 | },
69 | "colorID": 0
70 | },
71 | {
72 | "title": "NullExpression",
73 | "tags": "Tag",
74 | "body": "Test Line\n<>\nTest Line After",
75 | "position": {
76 | "x": 449,
77 | "y": 252
78 | },
79 | "colorID": 0
80 | },
81 | {
82 | "title": "Variable",
83 | "tags": "Tag",
84 | "body": "Test Line\n<><>\nTest Line After",
85 | "position": {
86 | "x": 449,
87 | "y": 252
88 | },
89 | "colorID": 0
90 | },
91 | {
92 | "title": "VariableExpression",
93 | "tags": "Tag",
94 | "body": "Test Line\n<><>\nTest Line After",
95 | "position": {
96 | "x": 449,
97 | "y": 252
98 | },
99 | "colorID": 0
100 | }
101 | ]
102 |
--------------------------------------------------------------------------------
/testFiles/bbcodeTags.yarn:
--------------------------------------------------------------------------------
1 | title: Start
2 | tags:
3 | colorID: 0
4 | position: 150,211
5 | ---
6 | [b]Bold[/b] text
7 | [u]Underlined[/u] text
8 | Text in [i]italics[/i]
9 | Text in [color=#ff0000]red color[/color] and [color=#0000ff]blue color[/color]
10 | [color=#00ff00][b]Green and bold[/b][/color] text
11 | [b][i]bold and italics[/i][/b]
12 | [b][u]bold and underlined[/u][/b]
13 | [i][u]italics and underlined[/u][/i]
14 | [i][u][color=#e900ff]italics, underlined and pink[/color][/u][/i]
15 | [img]image[/img]
16 | <>
17 | [[This link brings you to a new node:|link]]
18 | ===
19 | title: link
20 | tags:
21 | colorID: 0
22 | position: 482,212
23 | ---
24 | Empty Text
25 | ===
26 |
--------------------------------------------------------------------------------
/testFiles/commandsandfunctions.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "BasicCommands",
4 | "tags": "Tag",
5 | "body": "<>text in between commands<> <> <>",
6 | "position": {
7 | "x": 449,
8 | "y": 252
9 | },
10 | "colorID": 0
11 | },
12 | {
13 | "title": "StopCommand",
14 | "tags": "Tag",
15 | "body": "First line\n<>\nThis shouldn't show",
16 | "position": {
17 | "x": 449,
18 | "y": 252
19 | },
20 | "colorID": 0
21 | },
22 | {
23 | "title": "FunctionConditional",
24 | "tags": "Tag",
25 | "body": "First line\n<>This shouldn't show<>This should show<>After both",
26 | "position": {
27 | "x": 449,
28 | "y": 252
29 | },
30 | "colorID": 0
31 | },
32 | {
33 | "title": "VisitedFunction",
34 | "tags": "Tag",
35 | "body": "<>you have visited VisitedFunctionStart!<><>You have not visited SomeOtherNode!<>",
36 | "position": {
37 | "x": 449,
38 | "y": 252
39 | },
40 | "colorID": 0
41 | },
42 | {
43 | "title": "VisitedFunctionStart",
44 | "tags": "Tag",
45 | "body": "Hello[[VisitedFunction]]",
46 | "position": {
47 | "x": 449,
48 | "y": 252
49 | },
50 | "colorID": 0
51 | }
52 | ]
53 |
--------------------------------------------------------------------------------
/testFiles/conditions.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Start",
4 | "tags": "Tag",
5 | "body": "What are you?\n-> A troll\n <>\n-> A nice person\n <>\n[[Objective]]",
6 | "position": {
7 | "x": 449,
8 | "y": 252
9 | },
10 | "colorID": 0
11 | },
12 | {
13 | "title": "Objective",
14 | "tags": "Tag",
15 | "body": "<= 3>>\nBye…\n<>\nIs your objective clear?\n[[Yes|Objective.Yes]]\n[[No|Objective.No]]\n<>\n[[Maybe|Objective.Maybe]]\n<>\n<>\n",
16 | "position": {
17 | "x": 449,
18 | "y": 252
19 | },
20 | "colorID": 0
21 | },
22 | {
23 | "title": "Objective.No",
24 | "tags": "Tag",
25 | "body": "Blah blah blah blah\n[[Objective]]",
26 | "position": {
27 | "x": 449,
28 | "y": 252
29 | },
30 | "colorID": 0
31 | },
32 | {
33 | "title": "Objective.Yes",
34 | "tags": "Tag",
35 | "body": "Good let's start the mission.",
36 | "position": {
37 | "x": 449,
38 | "y": 252
39 | },
40 | "colorID": 0
41 | },
42 | {
43 | "title": "Objective.Maybe",
44 | "tags": "Tag",
45 | "body": "Are you trolling me?\n[[Objective]]",
46 | "position": {
47 | "x": 449,
48 | "y": 252
49 | },
50 | "colorID": 0
51 | },
52 |
53 | {
54 | "title": "BasicIf",
55 | "tags": "Tag",
56 | "body": "<>\nText before\n<>Inside if<>Text after",
57 | "position": {
58 | "x": 449,
59 | "y": 252
60 | },
61 | "colorID": 0
62 | },
63 | {
64 | "title": "BasicIfElse",
65 | "tags": "Tag",
66 | "body": "<>\nText before\n<>Inside if<>Inside else<>Text after",
67 | "position": {
68 | "x": 449,
69 | "y": 252
70 | },
71 | "colorID": 0
72 | },
73 | {
74 | "title": "BasicIfElseIf",
75 | "tags": "Tag",
76 | "body": "<>\nText before\n<>Inside if<>Inside elseif<>Text after",
77 | "position": {
78 | "x": 449,
79 | "y": 252
80 | },
81 | "colorID": 0
82 | },
83 | {
84 | "title": "BasicIfElseIfElse",
85 | "tags": "Tag",
86 | "body": "<>\nText before\n<>Inside if<>Inside elseif<>Inside else<>Text after",
87 | "position": {
88 | "x": 449,
89 | "y": 252
90 | },
91 | "colorID": 0
92 | }
93 | ]
94 |
--------------------------------------------------------------------------------
/testFiles/exportAsRenpyExample.json:
--------------------------------------------------------------------------------
1 | {
2 | "header": {
3 | "lastSavedUnix": "2022-11-02T18:02:33.452Z",
4 | "language": "en-GB",
5 | "documentType": "yarn",
6 | "markupLanguage": "bbcode",
7 | "filetypeVersion": "2",
8 | "pluginStorage": {
9 | "Runner": {}
10 | }
11 | },
12 | "nodes": [
13 | {
14 | "title": "livingRoom",
15 | "tags": "",
16 | "body": "// standard character text\nmc.c happy \"there's nothing interesting on tv today\"\n\n// standard narrator text\nHe couldn't find anything interesting on tv\n\n// wrap commands like this\n<>\n\n// To create a menu with two options, you need each option to be on a new line, followed by the question\nPlay some video games instead?\n[[yes|playsVideoGames]]\n[[no|gets up]]",
17 | "position": {
18 | "x": -274,
19 | "y": -1469
20 | },
21 | "colorID": 0
22 | },
23 | {
24 | "title": "playsVideoGames",
25 | "tags": "",
26 | "body": "mc \"I will play some video games\"\nSome time passes by",
27 | "position": {
28 | "x": -279,
29 | "y": -1688
30 | },
31 | "colorID": 0
32 | },
33 | {
34 | "title": "gets up",
35 | "tags": "",
36 | "body": "mc \"I am borded, let's do something else\"",
37 | "position": {
38 | "x": -42,
39 | "y": -1461
40 | },
41 | "colorID": 0
42 | },
43 | {
44 | "title": "MapUI()",
45 | "tags": "screen renpy",
46 | "body": "// if you put a screen tag in the node, it will be interpreted as a screen instead of a label\nimagebutton:\n xpos 618\n ypos 570\n idle \"BG/house1_idle.png\"\n hover \"BG/house1_hover.png\"\n action Call(\"call_livingRoomUI\")",
47 | "position": {
48 | "x": -510,
49 | "y": -1468
50 | },
51 | "colorID": 0
52 | },
53 | {
54 | "title": "call_livingRoomUI",
55 | "tags": "renpy",
56 | "body": "// if you put a renpy tag in the node, the text in the body will be interpreted as is, without trying to be converted to rpy\n$ current_location = \"livingRoom\"\n scene bg house_room\n call screen LivingRoomUI\n return",
57 | "position": {
58 | "x": -514,
59 | "y": -1687
60 | },
61 | "colorID": 0
62 | }
63 | ]
64 | }
--------------------------------------------------------------------------------
/testFiles/htmlTags.yarn:
--------------------------------------------------------------------------------
1 | title: Start
2 | tags:
3 | colorID: 0
4 | position: 108,232
5 | ---
6 | Bold text
7 | Underlined text
8 | Text in italics
9 | Text in red color and blue color
10 | Green and bold text
11 | bold and italics
12 | bold and underlined
13 | italics and underlined
14 | italics, underlined and pink
15 |
image
16 | <>
17 | [[This link brings you to a new node:|link]]
18 | ===
19 | title: link
20 | tags:
21 | colorID: 0
22 | position: 526,227
23 | ---
24 | Empty Text
25 | ===
26 |
--------------------------------------------------------------------------------
/testFiles/links.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "OneNode",
4 | "tags": "Tag",
5 | "body": "This is a test line",
6 | "position": {
7 | "x": -200,
8 | "y": -65
9 | },
10 | "colorID": 0
11 | },
12 | {
13 | "title": "ThreeNodes",
14 | "tags": "",
15 | "body": "This is a test line\nThis is another test line[[Option1]]\n[[Option2]]",
16 | "position": {
17 | "x": 128,
18 | "y": 274
19 | },
20 | "colorID": 0
21 | },
22 | {
23 | "title": "Option1",
24 | "tags": "",
25 | "body": "This is Option1's test line",
26 | "position": {
27 | "x": 585,
28 | "y": -156
29 | },
30 | "colorID": 0
31 | },
32 | {
33 | "title": "Option2",
34 | "tags": "",
35 | "body": "This is Option2's test line",
36 | "position": {
37 | "x": 607,
38 | "y": 688
39 | },
40 | "colorID": 0
41 | },
42 | {
43 | "title": "NamedLink",
44 | "tags": "",
45 | "body": "This is a test line\nThis is another test line\n[[First choice|Option1]]\n[[Second choice|Option2]]",
46 | "position": {
47 | "x": 1089,
48 | "y": 116
49 | },
50 | "colorID": 0
51 | },
52 | {
53 | "title": "OneLinkPassthrough",
54 | "tags": "",
55 | "body": "First test line\n[[Option1]]",
56 | "position": {
57 | "x": 148,
58 | "y": -139
59 | },
60 | "colorID": 0
61 | },
62 | {
63 | "title": "LinkAfterShortcuts",
64 | "tags": "",
65 | "body": "First test line\n-> Shortcut 1\n\tThis is the first shortcut\n-> Shortcut 2\n\tThis is the second shortcut\n[[First link|Option1]][[Second link|Option2]]",
66 | "position": {
67 | "x": 584,
68 | "y": 265
69 | },
70 | "colorID": 0
71 | }
72 | ]
--------------------------------------------------------------------------------
/testFiles/shortcuts.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "NonNested",
4 | "tags": "Tag",
5 | "body": "This is a test line\n-> Option 1\n\tThis is the first option\n-> Option 2\n\tThis is the second option\nThis is after both options",
6 | "position": {
7 | "x": 449,
8 | "y": 252
9 | },
10 | "colorID": 0
11 | },
12 | {
13 | "title": "Nested",
14 | "tags": "Tag",
15 | "body": "text\n-> shortcut1\n\tText1\n\t-> nestedshortcut1\n\t\tNestedText1\n\t-> nestedshortcut2\n\t\tNestedText2\n-> shortcut2\n\tText2\nmore text",
16 | "position": {
17 | "x": 449,
18 | "y": 252
19 | },
20 | "colorID": 0
21 | },
22 | {
23 | "title": "Conditional",
24 | "tags": "Tag",
25 | "body": "This is a test line\n-> Option 1\n\tThis is the first option\n-> Option 2 <>\n\tThis is the second option\n-> Option 3\n\tThis is the third option\nThis is after both options",
26 | "position": {
27 | "x": 449,
28 | "y": 252
29 | },
30 | "colorID": 0
31 | }
32 | ]
33 |
--------------------------------------------------------------------------------
/testFiles/simple-xml-example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Start
4 |
5 | [[Node2]]
6 |
7 | 0
8 |
9 |
10 | Node2
11 |
12 | [[Node3]]
13 |
14 | 0
15 |
16 |
17 | Node3
18 |
19 | Empty Text
20 |
21 | 0
22 |
23 |
24 |
--------------------------------------------------------------------------------
/testFiles/yarnExample.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Start",
4 | "tags": "err erg",
5 | "body": "A: Hey, I'm a character in a script!\n<>\nB: And I am too! You are talking to me!\nB: What would you prefer to do next?\n\n[[Leave|Leave]]\n[[Learn more|LearnMore]]",
6 | "position": {
7 | "x": 283,
8 | "y": 207
9 | },
10 | "colorID": 0
11 | },
12 | {
13 | "title": "Leave",
14 | "tags": "ad ber",
15 | "body": "A: Oh, goodbye!\nB: You'll be back soon!",
16 | "position": {
17 | "x": 190,
18 | "y": 539
19 | },
20 | "colorID": 0
21 | },
22 | {
23 | "title": "LearnMore",
24 | "tags": "rawText",
25 | "body": "A: HAHAHA\nBlah blah more..",
26 | "position": {
27 | "x": 534,
28 | "y": 534
29 | },
30 | "colorID": 0
31 | }
32 | ]
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebPackPlugin = require('html-webpack-plugin');
4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7 | const PreloadWebpackPlugin = require('preload-webpack-plugin');
8 | const CssUrlRelativePlugin = require('css-url-relative-plugin');
9 | const WebpackPwaManifest = require('webpack-pwa-manifest');
10 | const WorkboxPlugin = require('workbox-webpack-plugin');
11 |
12 | const IS_DEV = process.env.NODE_ENV === 'dev';
13 |
14 | const config = {
15 | mode: IS_DEV ? 'development' : 'production',
16 | devtool: IS_DEV ? 'eval' : 'source-map',
17 | entry: path.resolve(__dirname, 'src', 'js', 'index.js'),
18 | output: {
19 | filename: 'js/[name].[hash].js',
20 | path: path.resolve(__dirname, 'dist'),
21 | },
22 | node: {
23 | fs: 'empty',
24 | },
25 | module: {
26 | rules: [
27 | {
28 | test: /\.js$/,
29 | exclude: /node_modules/,
30 | loader: 'babel-loader',
31 | },
32 | {
33 | test: /\.(css)$/,
34 | use: [
35 | {
36 | loader: MiniCssExtractPlugin.loader,
37 | options: {
38 | hmr: IS_DEV,
39 | },
40 | },
41 | 'css-loader',
42 | // 'sass-loader',
43 | ],
44 | },
45 | {
46 | test: /\.(gif|png|jpe?g|svg)$/i,
47 | use: [
48 | {
49 | loader: 'url-loader',
50 | options: {
51 | limit: 1024,
52 | name: '[name].[ext]',
53 | fallback: 'file-loader',
54 | outputPath: 'public/images',
55 | },
56 | },
57 | {
58 | loader: 'image-webpack-loader',
59 | options: {
60 | mozjpeg: {
61 | progressive: true,
62 | quality: 65,
63 | },
64 | pngquant: {
65 | quality: '65-90',
66 | speed: 4,
67 | },
68 | gifsicle: {
69 | interlaced: false,
70 | },
71 | webp: {
72 | quality: 75,
73 | },
74 | },
75 | },
76 | ],
77 | },
78 | {
79 | test: /\.(ttf|eot|woff|woff2|ico)$/,
80 | use: {
81 | loader: 'file-loader',
82 | options: {
83 | name: 'fonts/[name].[ext]',
84 | },
85 | },
86 | },
87 | ],
88 | },
89 | plugins: [
90 | new CleanWebpackPlugin(),
91 | new webpack.ProvidePlugin({
92 | $: 'jquery',
93 | jQuery: 'jquery',
94 | 'windows.jQuery': 'jquery',
95 | Util: 'exports-loader?Util!bootstrap/js/dist/util',
96 | ko: 'exports-loader?!knockout',
97 | }),
98 | new CopyWebpackPlugin([
99 | {
100 | from: path.resolve(__dirname, 'src', 'public'),
101 | to: 'public',
102 | },
103 | ]),
104 | new MiniCssExtractPlugin({
105 | filename: IS_DEV ? 'css/[name].css' : 'css/[name].[contenthash].css',
106 | chunkFilename: 'css/[id].css',
107 | }),
108 | new webpack.HashedModuleIdsPlugin(),
109 | new PreloadWebpackPlugin({
110 | include: 'initial',
111 | }),
112 | new CssUrlRelativePlugin(),
113 | new WebpackPwaManifest({
114 | filename: 'manifest.json',
115 | // start_url: 'YarnClassic/.',
116 | inject: true,
117 | fingerprints: false,
118 | name: 'Yarn Story Editor',
119 | short_name: 'Yarn',
120 | description: 'Yarn Story Editor',
121 | background_color: '#3367D6',
122 | theme_color: '#3367D6',
123 | display: 'fullscreen',
124 | crossorigin: 'use-credentials', //can be null, use-credentials or anonymous
125 | icons: [
126 | {
127 | src: path.resolve('src/public/icon.png'),
128 | sizes: [96, 128, 192, 512], // multiple sizes, 192 needed by pwa
129 | },
130 | {
131 | src: path.resolve('src/public/icon.png'),
132 | sizes: [96, 128, 192, 512], // multiple sizes, 192 and 144 needed by pwa
133 | purpose: 'maskable'
134 | },
135 | {
136 | src: path.resolve('src/public/icon.ico'),
137 | sizes: [32], // you can also use the specifications pattern
138 | },
139 | ],
140 | share_target: {
141 | // action: 'share-target',
142 | // enctype: 'multipart/form-data',
143 | // method: 'POST', //github.io does not allow post
144 | // params: {
145 | // files: [{
146 | // name: 'image',
147 | // accept: ['image/*']
148 | // }]
149 | // }
150 | action: '/YarnClassic/',
151 | method: 'GET',
152 | enctype: 'application/x-www-form-urlencoded',
153 | params: {
154 | title: 'title',
155 | text: 'text',
156 | url: 'url',
157 | },
158 | },
159 | }),
160 | new HtmlWebPackPlugin({
161 | template: path.resolve(__dirname, './src/index.html'),
162 | favicon: path.resolve('src/public/icon.ico'),
163 | minify: {
164 | collapseWhitespace: true,
165 | removeComments: false, // This is mandatory, due to knockout's virtual bindings
166 | useShortDoctype: true,
167 | },
168 | }),
169 | // new WorkboxPlugin.GenerateSW({
170 | // swDest: path.resolve(__dirname, 'dist', 'sw.js'),
171 | // exclude: [/\.map$/, /_redirects/],
172 | // runtimeCaching: [{
173 | // urlPattern: /https:\/\/yarnspinnertool\.github\.io\/YarnClassic\//,
174 | // handler: 'NetworkFirst' //CacheFirst
175 | // }],
176 | // }),
177 | new WorkboxPlugin.InjectManifest({
178 | swDest: path.resolve(__dirname, 'dist', 'sw.js'),
179 | exclude: [/\.map$/, /_redirects/],
180 | swSrc: path.resolve(__dirname, 'src', 'sw-src.js'),
181 | }),
182 | ],
183 | devServer: {
184 | contentBase: path.join(__dirname, 'src'),
185 | watchContentBase: true,
186 | host: '0.0.0.0', //this will allow you to run it on a smartphone with 8080 port. Use ipconfig or ifconfig to see broadcast address
187 | },
188 | optimization: {
189 | runtimeChunk: 'single',
190 | splitChunks: {
191 | cacheGroups: {
192 | vendor: {
193 | test: /node_modules/,
194 | chunks: 'initial',
195 | name: 'vendor',
196 | priority: 10,
197 | enforce: true,
198 | },
199 | },
200 | },
201 | minimizer: [],
202 | },
203 | };
204 |
205 | if (!IS_DEV) {
206 | const TerserPlugin = require('terser-webpack-plugin');
207 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
208 | config.optimization.minimizer.push(
209 | new TerserPlugin(),
210 | new OptimizeCSSAssetsPlugin({})
211 | );
212 | }
213 |
214 | module.exports = config;
215 |
--------------------------------------------------------------------------------