From bf6796abf3778ac44661bab6f549919c67efc82a Mon Sep 17 00:00:00 2001 From: Lane Sun Date: Thu, 29 Aug 2024 23:17:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90DSL?= =?UTF-8?q?=E8=AF=AD=E8=A8=80Tee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/plugin/basic/rule/editor.js | 57 +- src/core/plugin/basic/rule/layout.js | 87 +-- src/core/plugin/basic/types.plugin.js | 77 +- src/core/plugin/json/rule/action.js | 687 +++++++++--------- src/core/plugin/json/type/boolean.js | 72 +- src/core/plugin/json/type/key.js | 45 +- src/core/plugin/json/type/null.js | 34 +- src/core/plugin/json/type/number.js | 115 ++- src/core/plugin/json/type/string.js | 112 +-- src/core/plugin/json/types.plugin.js | 79 +- ...dex.plugin.js => index.plugin.disabled.js} | 0 src/core/plugin/tee/dsl/html.plugin.js | 16 + src/core/plugin/tee/dsl/val/boolean.js | 12 + src/core/plugin/tee/dsl/val/number.js | 12 + src/core/plugin/tee/dsl/val/string.js | 12 + src/core/plugin/tee/dsl/val/types.plugin.js | 13 + src/core/plugin/tee/index.plugin.js | 48 ++ src/core/plugin/tee/rule/action.js | 130 ++++ src/core/plugin/tee/rule/keymap.js | 1 + src/core/plugin/tee/style/node.css | 11 + src/core/plugin/tee/trans.plugin.js | 15 + src/core/plugin/tee/type/node.js | 157 ++++ src/core/plugin/tee/types.plugin.js | 33 + src/core/plugin/type-selector/index.plugin.js | 58 +- src/main.js | 157 ++-- src/plugins.cfg | 36 +- 26 files changed, 1263 insertions(+), 813 deletions(-) rename src/core/plugin/stash/{index.plugin.js => index.plugin.disabled.js} (100%) create mode 100644 src/core/plugin/tee/dsl/html.plugin.js create mode 100644 src/core/plugin/tee/dsl/val/boolean.js create mode 100644 src/core/plugin/tee/dsl/val/number.js create mode 100644 src/core/plugin/tee/dsl/val/string.js create mode 100644 src/core/plugin/tee/dsl/val/types.plugin.js create mode 100644 src/core/plugin/tee/index.plugin.js create mode 100644 src/core/plugin/tee/rule/action.js create mode 100644 src/core/plugin/tee/rule/keymap.js create mode 100644 src/core/plugin/tee/style/node.css create mode 100644 src/core/plugin/tee/trans.plugin.js create mode 100644 src/core/plugin/tee/type/node.js create mode 100644 src/core/plugin/tee/types.plugin.js diff --git a/src/core/plugin/basic/rule/editor.js b/src/core/plugin/basic/rule/editor.js index 0f40137d..89b74908 100644 --- a/src/core/plugin/basic/rule/editor.js +++ b/src/core/plugin/basic/rule/editor.js @@ -1,20 +1,39 @@ -export default { - ".core:editor": { - "modifiers": { - "modify": class extends TTModer.Map { - modify(node, offset, delete_count, inserts) { - super.modify(node); - const deletes = node.inner.modify(offset, delete_count, inserts); - this.data_src = [offset, inserts.length, deletes]; - } - }, - "move": class extends TTModer.Map { - modify(node, offset, count, delta) { - super.modify(node); - this.data_src = [offset + delta, count, -delta]; - node.inner.move(offset, count, delta); - } - }, - }, +export default (plugin) => ({ + ".core:editor": { + modifiers: { + modify: class extends TTModer.Map { + modify(node, offset, delete_count, inserts) { + super.modify(node); + const deletes = node.inner.modify(offset, delete_count, inserts); + this.data_src = [offset, inserts.length, deletes]; + } + }, + move: class extends TTModer.Map { + modify(node, offset, count, delta) { + super.modify(node); + this.data_src = [offset + delta, count, -delta]; + node.inner.move(offset, count, delta); + } + }, }, -} \ No newline at end of file + }, + + ".core:editor > .core:selection": { + actions: { + "core:switch": class extends TTAction { + static name = Names("Switch"); + static icon = "switch"; + static unique = true; + static async call(sel) { + const node = sel.parent; + const nnode = await plugin.request_insert(sel); + if (nnode) { + const [offset] = sel.data_range; + node.mod("modify", offset, 1, [nnode]); + sel.set(offset, offset + 1); + } + } + }, + }, + }, +}); diff --git a/src/core/plugin/basic/rule/layout.js b/src/core/plugin/basic/rule/layout.js index 75823e00..8de3b83d 100644 --- a/src/core/plugin/basic/rule/layout.js +++ b/src/core/plugin/basic/rule/layout.js @@ -1,45 +1,46 @@ export default { - ".core:editor": { - "able.core:layout.select": true, - "handles.core:layout": { - "get-selection"(p, anchor_node, focus_node) { - let anchor = this.inner.indexOf(anchor_node); - let focus = this.inner.indexOf(focus_node); - if (anchor <= focus) { - focus++; - } else { - anchor++; - } - return this.$type["#core:selection"]({ - scope: this, - anchor, focus, - }); - }, - }, - "handles.core:selection": { - "select"(p, sel, node) { - sel.data_scope.val = this; - const offset = this.inner.indexOf(node); - sel.set(offset, offset + 1); - }, - "resolve"(p, {anchor, focus}) { - return ["range", this.inner.slice(...[anchor, focus].num_sorted())]; - }, - "dir"(p) { - return {}; - }, - "collapsed"(p) { - return false; - }, - "side"(p, dir, pos) { - return pos; - }, - "move"(p, dir, pos) { - return pos; - }, - "varify"(p, pos) { - return false; - }, - }, + ".core:editor": { + "able.core:layout.select": true, + "handles.core:layout": { + "get-selection"(p, anchor_node, focus_node) { + let anchor = this.inner.indexOf(anchor_node); + let focus = this.inner.indexOf(focus_node); + if (anchor <= focus) { + focus++; + } else { + anchor++; + } + return this.$type["#core:selection"]({ + scope: this, + anchor, + focus, + }); + }, }, -} \ No newline at end of file + "handles.core:selection": { + select(p, sel, node) { + sel.data_scope.val = this; + const offset = this.inner.indexOf(node); + sel.set(offset, offset + 1); + }, + resolve(p, { anchor, focus }) { + return ["range", this.inner.slice(...[anchor, focus].num_sorted())]; + }, + dir(p) { + return {}; + }, + collapsed(p) { + return false; + }, + side(p, dir, pos) { + return pos; + }, + move(p, dir, pos) { + return pos; + }, + varify(p, pos) { + return false; + }, + }, + }, +}; diff --git a/src/core/plugin/basic/types.plugin.js b/src/core/plugin/basic/types.plugin.js index 99710c0f..8b4d7cc4 100644 --- a/src/core/plugin/basic/types.plugin.js +++ b/src/core/plugin/basic/types.plugin.js @@ -1,41 +1,48 @@ -const id = "#types:core:basic" -const provides = [".types:core:basic", ".types"] -const requires = { -} +const id = "#types:core:basic"; +const provides = [".types:core:basic", ".types"]; +const requires = {}; export default class extends TTPlugin { - static id = id - static provides = provides - static requires(plugins) { - return this.req_essential(plugins, requires); - } + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } - ".core:type-loader" = [ - "type/frame", - "type/text-field", - "type/selection", - "type/vector-cursor", - "type/vector-range", - "type/data/base", - "type/data/atom", - "type/data/seq", - "type/pattern/base", - "type/pattern/atom", - "type/pattern/seq", - ] + ".core:type-loader" = [ + "type/frame", + "type/text-field", + "type/selection", + "type/vector-cursor", + "type/vector-range", + "type/data/base", + "type/data/atom", + "type/data/seq", + "type/pattern/base", + "type/pattern/atom", + "type/pattern/seq", + ]; - ".core:rule-loader" = [ - "rule/editor", - "rule/keymap", - ] + ".core:rule-loader" = ["rule/editor", "rule/keymap"]; - ".core:style-loader" = [ - "style/context-menu", - "style/cursor", - "style/frame", - "style/layout", - "style/range", - "style/selector", - "style/text-field", - ] + ".core:style-loader" = [ + "style/context-menu", + "style/cursor", + "style/frame", + "style/layout", + "style/range", + "style/selector", + "style/text-field", + ]; + + async request_insert(anchor) { + const id = await this.root.$require[".core:type-selector"].request(anchor); + if (id) { + const Node = this.root.$type[id]; + const nnode = Node(); + return nnode; + } else { + return null; + } + } } diff --git a/src/core/plugin/json/rule/action.js b/src/core/plugin/json/rule/action.js index 85c95943..93ca337e 100644 --- a/src/core/plugin/json/rule/action.js +++ b/src/core/plugin/json/rule/action.js @@ -1,373 +1,338 @@ -export default plugin => ({ - ".json:boolean": { - "actions": { - "core:toggle": class extends TTAction { - static name = Names("Toggle") - static icon = "toggle-left" - static call(node) { - node.mod("toggle"); - } - }, - }, - }, - - ".json:string": { - "actions": { - "core:select-all": class extends TTAction { - static name = Names("Select All") - static icon = "select-all" - static unique = true - static call(node) { - node.select_all(); - } - }, - }, - }, - - ".json:number": { - "actions": { - }, +export default (plugin) => ({ + ".json:boolean": { + actions: { + "core:toggle": class extends TTAction { + static name = Names("Toggle"); + static icon = "toggle-left"; + static call(node) { + node.mod("toggle"); + } + }, }, + }, - ".json:null": { - "actions": { - }, + ".json:string": { + actions: { + "core:select-all": class extends TTAction { + static name = Names("Select All"); + static icon = "select-all"; + static unique = true; + static call(node) { + node.select_all(); + } + }, }, + }, - ".json:array": { - "actions": { - "core:insert-into": class extends TTAction { - static name = Names("Insert Into") - static icon = "plus" - static unique = true - static async call(node) { - const nnode = await plugin.request_insert(node); - if (nnode) { - const offset = node.data.length; - node.mod("modify", offset, 0, [nnode]); - } - } - }, - "core:column-increase": class extends TTAction { - static name = Names("Column Increase") - static icon = "viewport-wide" - static varify(node) { - return node.data_column.val < node.data.length; - } - static call(node) { - node.data_column.val += 1; - } - }, - "core:column-decrease": class extends TTAction { - static name = Names("Column Decrease") - static icon = "viewport-narrow" - static varify(node) { - return node.data_column.val > 1; - } - static call(node) { - node.data_column.val -= 1; - } - }, - "core:restruct-rows": class extends TTAction { - static name = Names("Restruct Rows") - static icon = "brackets-contain" - static varify(node) { - return node.data.length > 0; - } - static call(node) { - const Array = node.$type[".json:array"]; - const column = node.data_column.val; - const data = [...node.data]; - const group = []; - node.mod("modify", 0, data.length, []); - for (let i = 0; i < data.length; i += column) { - group.push(data.slice(i, i + column)); - } - const ndata = group.map(_ => Array([])); - node.mod("modify", 0, 0, ndata); - node.data_column.val = 1; - group.map((data, i) => { - ndata[i].mod("modify", 0, 0, data); - ndata[i].data_column.val = column; - }); - } - }, - "core:clear": class extends TTAction { - static name = Names("Clear") - static icon = "eraser" - static varify(node) { - return node.data.length > 0; - } - static call(node) { - node.mod("modify", 0, node.data.length, []); - } - }, - }, + ".json:array": { + actions: { + "core:insert-into": class extends TTAction { + static name = Names("Insert Into"); + static icon = "plus"; + static unique = true; + static async call(node) { + const nnode = await plugin.request_insert(node); + if (nnode) { + const offset = node.data.length; + node.mod("modify", offset, 0, [nnode]); + } + } + }, + "core:column-increase": class extends TTAction { + static name = Names("Column Increase"); + static icon = "viewport-wide"; + static varify(node) { + return node.data_column.val < node.data.length; + } + static call(node) { + node.data_column.val += 1; + } + }, + "core:column-decrease": class extends TTAction { + static name = Names("Column Decrease"); + static icon = "viewport-narrow"; + static varify(node) { + return node.data_column.val > 1; + } + static call(node) { + node.data_column.val -= 1; + } + }, + "core:restruct-rows": class extends TTAction { + static name = Names("Restruct Rows"); + static icon = "brackets-contain"; + static varify(node) { + return node.data.length > 0; + } + static call(node) { + const Array = node.$type[".json:array"]; + const column = node.data_column.val; + const data = [...node.data]; + const group = []; + node.mod("modify", 0, data.length, []); + for (let i = 0; i < data.length; i += column) { + group.push(data.slice(i, i + column)); + } + const ndata = group.map((_) => Array([])); + node.mod("modify", 0, 0, ndata); + node.data_column.val = 1; + group.map((data, i) => { + ndata[i].mod("modify", 0, 0, data); + ndata[i].data_column.val = column; + }); + } + }, + "core:clear": class extends TTAction { + static name = Names("Clear"); + static icon = "eraser"; + static varify(node) { + return node.data.length > 0; + } + static call(node) { + node.mod("modify", 0, node.data.length, []); + } + }, }, + }, - ".json:array > .core:selection": { - "actions": { - "core:insert": class extends TTAction { - static name = Names("Insert") - static icon = "plus" - static unique = true - static async call(sel, nnode) { - const node = sel.parent; - nnode ??= await plugin.request_insert(sel); - if (nnode) { - const [, offset] = sel.data_range; - node.mod("modify", offset, 0, [nnode]); - sel.set(offset, offset + 1); - } - } - }, - "core:switch": class extends TTAction { - static name = Names("Switch") - static icon = "switch" - static unique = true - static varify(sel) { - return sel.data_nodes.length === 1; - } - static async call(sel) { - const node = sel.parent; - const nnode = await plugin.request_insert(sel); - if (nnode) { - const [offset] = sel.data_range; - node.mod("modify", offset, 1, [nnode]); - sel.set(offset, offset + 1); - } - } - }, - "core:select-all": class extends TTAction { - static name = Names("Select All") - static icon = "select-all" - static unique = true - static call(sel) { - const node = sel.parent; - sel.set(0, node.data.length); - } - }, - "core:restruct": class extends TTAction { - static name = Names("Restruct") - static icon = "brackets-contain" - static varify(sel) { - return !sel.collapsed(); - } - static call(sel) { - const node = sel.parent; - const Array = node.$type[".json:array"]; - const [start, end] = sel.data_range.num_sorted(); - const data = node.data.slice(start, end); - node.mod("modify", start, end - start, []); - const nnode = Array([]); - node.mod("modify", start, 0, [nnode]); - nnode.mod("modify", 0, 0, data); - sel.set(start, start + 1); - } - }, - "core:extract": class extends TTAction { - static name = Names("Extract") - static icon = "dots" - static varify(sel) { - const node = sel.parent; - const [start, end] = sel.data_range.num_sorted(); - return node.data.slice(start, end).some(n => n.is(".json:array")); - } - static call(sel) { - const node = sel.parent; - const [start, end] = sel.data_range.num_sorted(); - const data = []; - for (let i = start; i < end; i++) { - const n = node.data[i]; - if (n.is(".json:array")) { - data.push(...n.data); - n.mod("modify", 0, n.data.length, []); - } else { - data.push(n); - } - } - node.mod("modify", start, end - start, data); - sel.set(start, start + data.length); - } - }, - "core:delete": class extends TTAction { - static name = Names("Delete") - static icon = "trash" - static varify(sel) { - return !sel.collapsed(); - } - static call(sel) { - const node = sel.parent; - const [start, end] = sel.data_range.num_sorted(); - node.mod("modify", start, end - start, []); - sel.set(start, start); - } - }, - }, + ".json:array > .core:selection": { + actions: { + "core:insert": class extends TTAction { + static name = Names("Insert"); + static icon = "plus"; + static unique = true; + static async call(sel, nnode) { + const node = sel.parent; + nnode ??= await plugin.request_insert(sel); + if (nnode) { + const [, offset] = sel.data_range; + node.mod("modify", offset, 0, [nnode]); + sel.set(offset, offset + 1); + } + } + }, + "core:switch": class extends TTAction { + static name = Names("Switch"); + static icon = "switch"; + static unique = true; + static varify(sel) { + return sel.data_nodes.length === 1; + } + static async call(sel) { + const node = sel.parent; + const nnode = await plugin.request_insert(sel); + if (nnode) { + const [offset] = sel.data_range; + node.mod("modify", offset, 1, [nnode]); + sel.set(offset, offset + 1); + } + } + }, + "core:select-all": class extends TTAction { + static name = Names("Select All"); + static icon = "select-all"; + static unique = true; + static call(sel) { + const node = sel.parent; + sel.set(0, node.data.length); + } + }, + "core:restruct": class extends TTAction { + static name = Names("Restruct"); + static icon = "brackets-contain"; + static varify(sel) { + return !sel.collapsed(); + } + static call(sel) { + const node = sel.parent; + const Array = node.$type[".json:array"]; + const [start, end] = sel.data_range.num_sorted(); + const data = node.data.slice(start, end); + node.mod("modify", start, end - start, []); + const nnode = Array([]); + node.mod("modify", start, 0, [nnode]); + nnode.mod("modify", 0, 0, data); + sel.set(start, start + 1); + } + }, + "core:extract": class extends TTAction { + static name = Names("Extract"); + static icon = "dots"; + static varify(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + return node.data.slice(start, end).some((n) => n.is(".json:array")); + } + static call(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + const data = []; + for (let i = start; i < end; i++) { + const n = node.data[i]; + if (n.is(".json:array")) { + data.push(...n.data); + n.mod("modify", 0, n.data.length, []); + } else { + data.push(n); + } + } + node.mod("modify", start, end - start, data); + sel.set(start, start + data.length); + } + }, + "core:delete": class extends TTAction { + static name = Names("Delete"); + static icon = "trash"; + static varify(sel) { + return !sel.collapsed(); + } + static call(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + node.mod("modify", start, end - start, []); + sel.set(start, start); + } + }, }, + }, - ".json:object": { - "actions": { - "core:insert-into": class extends TTAction { - static name = Names("Insert Into") - static icon = "plus" - static unique = true - static async call(node) { - const { - ".json:key": Key, - ".json:null": Null, - } = node.$type; - const offset = node.data.length; - const key = Key(); - node.mod("modify_entries", offset, 0, [[key, Null()]]); - key.request("core:active"); - } - }, - "core:clear": class extends TTAction { - static name = Names("Clear") - static icon = "eraser" - static varify(node) { - return node.data.length > 0; - } - static call(node) { - node.mod("modify_entries", 0, node.data.length, []); - } - }, - }, + ".json:object": { + actions: { + "core:insert-into": class extends TTAction { + static name = Names("Insert Into"); + static icon = "plus"; + static unique = true; + static async call(node) { + const { ".json:key": Key, ".json:null": Null } = node.$type; + const offset = node.data.length; + const key = Key(); + node.mod("modify_entries", offset, 0, [[key, Null()]]); + key.request("core:active"); + } + }, + "core:clear": class extends TTAction { + static name = Names("Clear"); + static icon = "eraser"; + static varify(node) { + return node.data.length > 0; + } + static call(node) { + node.mod("modify_entries", 0, node.data.length, []); + } + }, }, + }, - ".json:object > .core:selection": { - "actions": { - "core:insert": class extends TTAction { - static name = Names("Insert") - static icon = "plus" - static unique = true - static async call(sel, nnode) { - const node = sel.parent; - const { - ".json:key": Key, - ".json:null": Null, - } = node.$type; - const [, [offset]] = sel.data_range; - const key = Key(); - node.mod("modify_entries", offset, 0, [[key, nnode ?? Null()]]); - sel.set([offset, 0], [offset + 1, 1]); - key.request("core:active"); - } - }, - "core:switch": class extends TTAction { - static name = Names("Switch") - static icon = "switch" - static unique = true - static varify(sel) { - return sel.data_nodes.length === 1; - } - static async call(sel) { - const node = sel.parent; - const target = sel.data_nodes.val; - const [[r1], [r2]] = sel.data_range; - const [start, end] = [r1, r2].num_sorted(); - if (target.is(".json:key")) { - sel.set([start, 1], [end, 2]); - } else { - const nnode = await plugin.request_insert(sel); - if (nnode) { - node.mod("modify_value", start, nnode); - sel.set([start, 1], [end, 2]); - } - } - } - }, - "core:delete": class extends TTAction { - static name = Names("Delete") - static icon = "trash" - static varify(sel) { - return !sel.collapsed(); - } - static call(sel) { - const node = sel.parent; - const [[r1], [r2]] = sel.data_range; - const [start, end] = [r1, r2].num_sorted(); - node.mod("modify_entries", start, end - start, []); - sel.set([start, 0], [start, 2]); - } - }, - "core:select-all": class extends TTAction { - static name = Names("Select All") - static icon = "select-all" - static unique = true - static call(sel) { - const node = sel.parent; - sel.set([0, 0], [node.data.length, 2]); - } - }, - "core:extract": class extends TTAction { - static name = Names("Extract") - static icon = "dots" - static varify(sel) { - const node = sel.parent; - const [[r1], [r2]] = sel.data_range; - const [start, end] = [r1, r2].num_sorted(); - return node.data.slice(start, end).some(n => n[1].is(".json:object")); - } - static call(sel) { - const node = sel.parent; - const [[r1], [r2]] = sel.data_range; - const [start, end] = [r1, r2].num_sorted(); - const data = []; - for (let i = start; i < end; i++) { - const [, n] = node.data[i]; - if (n.is(".json:object")) { - data.push(...n.data); - n.mod("modify_entries", 0, n.data.length, []); - } else { - data.push(node.data[i]); - } - } - node.mod("modify_entries", start, end - start, data); - sel.set([start, 0], [start + data.length, 2]); - } - }, - "core:restruct": class extends TTAction { - static name = Names("Restruct") - static icon = "code-dots" - static varify(sel) { - return !sel.collapsed(); - } - static call(sel) { - const node = sel.parent; - const { - ".json:object": Object, - ".json:key": Key, - } = node.$type; - const [[r1], [r2]] = sel.data_range; - const [start, end] = [r1, r2].num_sorted(); - const data = node.data.slice(start, end); - node.mod("modify_entries", start, end - start, []); - const nnode = Object([]); - node.mod("modify_entries", start, 0, [[Key([""]), nnode]]); - nnode.mod("modify_entries", 0, 0, data); - sel.set([r1, 1], [r1 + 1, 2]); - } - }, - }, - }, - ".core:editor > .core:selection": { - "actions": { - "core:switch": class extends TTAction { - static name = Names("Switch") - static icon = "switch" - static unique = true - static async call(sel) { - const node = sel.parent; - const nnode = await plugin.request_insert(sel); - if (nnode) { - const [offset] = sel.data_range; - node.mod("modify", offset, 1, [nnode]); - sel.set(offset, offset + 1); - } - } - }, + ".json:object > .core:selection": { + actions: { + "core:insert": class extends TTAction { + static name = Names("Insert"); + static icon = "plus"; + static unique = true; + static async call(sel, nnode) { + const node = sel.parent; + const { ".json:key": Key, ".json:null": Null } = node.$type; + const [, [offset]] = sel.data_range; + const key = Key(); + node.mod("modify_entries", offset, 0, [[key, nnode ?? Null()]]); + sel.set([offset, 0], [offset + 1, 1]); + key.request("core:active"); + } + }, + "core:switch": class extends TTAction { + static name = Names("Switch"); + static icon = "switch"; + static unique = true; + static varify(sel) { + return sel.data_nodes.length === 1; + } + static async call(sel) { + const node = sel.parent; + const target = sel.data_nodes.val; + const [[r1], [r2]] = sel.data_range; + const [start, end] = [r1, r2].num_sorted(); + if (target.is(".json:key")) { + sel.set([start, 1], [end, 2]); + } else { + const nnode = await plugin.request_insert(sel); + if (nnode) { + node.mod("modify_value", start, nnode); + sel.set([start, 1], [end, 2]); + } + } + } + }, + "core:delete": class extends TTAction { + static name = Names("Delete"); + static icon = "trash"; + static varify(sel) { + return !sel.collapsed(); + } + static call(sel) { + const node = sel.parent; + const [[r1], [r2]] = sel.data_range; + const [start, end] = [r1, r2].num_sorted(); + node.mod("modify_entries", start, end - start, []); + sel.set([start, 0], [start, 2]); + } + }, + "core:select-all": class extends TTAction { + static name = Names("Select All"); + static icon = "select-all"; + static unique = true; + static call(sel) { + const node = sel.parent; + sel.set([0, 0], [node.data.length, 2]); + } + }, + "core:extract": class extends TTAction { + static name = Names("Extract"); + static icon = "dots"; + static varify(sel) { + const node = sel.parent; + const [[r1], [r2]] = sel.data_range; + const [start, end] = [r1, r2].num_sorted(); + return node.data + .slice(start, end) + .some((n) => n[1].is(".json:object")); + } + static call(sel) { + const node = sel.parent; + const [[r1], [r2]] = sel.data_range; + const [start, end] = [r1, r2].num_sorted(); + const data = []; + for (let i = start; i < end; i++) { + const [, n] = node.data[i]; + if (n.is(".json:object")) { + data.push(...n.data); + n.mod("modify_entries", 0, n.data.length, []); + } else { + data.push(node.data[i]); + } + } + node.mod("modify_entries", start, end - start, data); + sel.set([start, 0], [start + data.length, 2]); + } + }, + "core:restruct": class extends TTAction { + static name = Names("Restruct"); + static icon = "code-dots"; + static varify(sel) { + return !sel.collapsed(); + } + static call(sel) { + const node = sel.parent; + const { ".json:object": Object, ".json:key": Key } = node.$type; + const [[r1], [r2]] = sel.data_range; + const [start, end] = [r1, r2].num_sorted(); + const data = node.data.slice(start, end); + node.mod("modify_entries", start, end - start, []); + const nnode = Object([]); + node.mod("modify_entries", start, 0, [[Key([""]), nnode]]); + nnode.mod("modify_entries", 0, 0, data); + sel.set([r1, 1], [r1 + 1, 2]); } + }, }, -}) \ No newline at end of file + }, +}); diff --git a/src/core/plugin/json/type/boolean.js b/src/core/plugin/json/type/boolean.js index ec00962f..444b92cf 100644 --- a/src/core/plugin/json/type/boolean.js +++ b/src/core/plugin/json/type/boolean.js @@ -5,46 +5,42 @@ const name = Names("Boolean"); const Super = await TTNode.Class(extend); export default class extends Super { - static id = id - static provides = provides - static uses = [id, ...provides, ...Super.uses] - static name = name + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; - static rule = { - "modifiers": { - "toggle": class extends TTModer.Sym { - modify(node) { - super.modify(node); - node.data.val = !node.data.val; - } - }, + static rule = { + modifiers: { + toggle: class extends TTModer.Sym { + modify(node) { + super.modify(node); + node.data.val = !node.data.val; + } + }, + }, + handles: { + "core:selection": { + enter(p) { + this.mod("toggle"); }, - "handles": { - "core:selection": { - "enter"(p) { - this.mod("toggle"); - }, - }, - }, - } + }, + }, + }; - init(data) { - this.data = data ?? [false]; - } + init(data) { + this.data = data ?? [false]; + } - struct() { - const { - "#core:frame": frame, - } = this.$type; - return frame(this.data.bmap(v => v.toString())) - .into(this) - .name(name) - .color(200, 0.62) - .style_on("inline", "code") - .melem; - } + struct() { + const { "#core:frame": frame } = this.$type; + return frame(this.data.bmap((v) => v.toString())) + .into(this) + .color(200, 0.62) + .style_on("inline", "code").melem; + } - to_json() { - return this.data[0]; - } -} \ No newline at end of file + to_json() { + return this.data[0]; + } +} diff --git a/src/core/plugin/json/type/key.js b/src/core/plugin/json/type/key.js index d4554a27..e941e71a 100644 --- a/src/core/plugin/json/type/key.js +++ b/src/core/plugin/json/type/key.js @@ -5,30 +5,25 @@ const name = Names("Key"); const Super = await TTNode.Class(extend); export default class extends Super { - static id = id - static provides = provides - static uses = [id, ...provides, ...Super.uses] - static name = name + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; - init(data) { - this.data = data ?? ['']; - } + init(data) { + this.data = data ?? [""]; + } - struct($) { - const { - "#core:frame": frame, - "#core:text-field": field, - } = this.$type; - return frame([ - ME.div.class("core-key-label")( - $("field", field(this.data.bclone()).into(this)).melem, - ME.div.class("core-key-hint")('→'), - ), - ]) - .into(this) - .name(name) - .color(180, 0.5, 1.1) - .style_on("inline", "code") - .melem; - } -} \ No newline at end of file + struct($) { + const { "#core:frame": frame, "#core:text-field": field } = this.$type; + return frame([ + ME.div.class("core-key-label")( + $("field", field(this.data.bclone()).into(this)).melem, + ME.div.class("core-key-hint")("→"), + ), + ]) + .into(this) + .color(180, 0.5, 1.1) + .style_on("inline", "code").melem; + } +} diff --git a/src/core/plugin/json/type/null.js b/src/core/plugin/json/type/null.js index 466ab53c..529a4809 100644 --- a/src/core/plugin/json/type/null.js +++ b/src/core/plugin/json/type/null.js @@ -5,24 +5,20 @@ const name = Names("Null"); const Super = await TTNode.Class(extend); export default class extends Super { - static id = id - static provides = provides - static uses = [id, ...provides, ...Super.uses] - static name = name + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; - struct() { - const { - "#core:frame": frame, - } = this.$type; - return frame(["null"]) - .into(this) - .name(name) - .color(0, 0, 0.8) - .style_on("inline", "code") - .melem; - } + struct() { + const { "#core:frame": frame } = this.$type; + return frame(["null"]) + .into(this) + .color(0, 0, 0.8) + .style_on("inline", "code").melem; + } - to_json() { - return null; - } -} \ No newline at end of file + to_json() { + return null; + } +} diff --git a/src/core/plugin/json/type/number.js b/src/core/plugin/json/type/number.js index 9a210016..2789344d 100644 --- a/src/core/plugin/json/type/number.js +++ b/src/core/plugin/json/type/number.js @@ -5,68 +5,67 @@ const name = Names("Number"); const Super = await TTNode.Class(extend); export default class extends Super { - static id = id - static provides = provides - static uses = [id, ...provides, ...Super.uses] - static name = name + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; - static rule = { - "modifiers": { - "set": class extends TTModer.Map { - modify(node, value) { - super.modify(node); - this.data_src = [node.data.val]; - node.data.val = value; - } - }, + static rule = { + modifiers: { + set: class extends TTModer.Map { + modify(node, value) { + super.modify(node); + this.data_src = [node.data.val]; + node.data.val = value; + } + }, + }, + handles: { + "core:text-field": { + edit(p, content) { + const val = Number.parseFloat(content); + if (Number.isNaN(val)) { + this.data.val = this.data.val; + } else if (this.data.val.toString() !== content) { + this.mod("set", val); + } }, - "handles": { - "core:text-field": { - "edit"(p, content) { - const val = Number.parseFloat(content); - if (Number.isNaN(val)) { - this.data.val = this.data.val; - } else if (this.data.val.toString() !== content) { - this.mod("set", val); - } - }, - "escape"(p) { - this.root.focus(); - }, - }, - "core:active"(p) { - this.struct_ref("field").request_pack(p); - }, - "core:selection": { - "enter"(p) { - this.request("core:active"); - this.select_all(); - }, - }, + escape(p) { + this.root.focus(); }, - } + }, + "core:active"(p) { + this.struct_ref("field").request_pack(p); + }, + "core:selection": { + enter(p) { + this.request("core:active"); + this.select_all(); + }, + }, + }, + }; - init(data) { - this.data = data ?? [0]; - } + init(data) { + this.data = data ?? [0]; + } - struct($) { - const { - "#core:text-field": field, - } = this.$type; - return $("field", field(this.data.bmap(v => v.toString())) - .into(this) - .name(name) - .color(120, 0.5, 1.1) - .style_on("inline", "code", "t-number")) - .melem; - } + struct($) { + const { "#core:text-field": field } = this.$type; + return $( + "field", + field(this.data.bmap((v) => v.toString())) + .into(this) + .color(120, 0.5, 1.1) + .style_on("inline", "code", "t-number"), + ).melem; + } - to_json() { - return this.data[0]; - } + to_json() { + return this.data[0]; + } - select_all() { - this.struct_ref("field").select_all(); - } -} \ No newline at end of file + select_all() { + this.struct_ref("field").select_all(); + } +} diff --git a/src/core/plugin/json/type/string.js b/src/core/plugin/json/type/string.js index 2ba10f31..7814ba9a 100644 --- a/src/core/plugin/json/type/string.js +++ b/src/core/plugin/json/type/string.js @@ -5,66 +5,66 @@ const name = Names("String"); const Super = await TTNode.Class(extend); export default class extends Super { - static id = id - static provides = provides - static uses = [id, ...provides, ...Super.uses] - static name = name + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; - static rule = { - "modifiers": { - "set": class extends TTModer.Map { - modify(node, value) { - super.modify(node); - this.data_src = [node.data.val]; - node.data.val = value; - } - }, + static rule = { + modifiers: { + set: class extends TTModer.Map { + modify(node, value) { + super.modify(node); + this.data_src = [node.data.val]; + node.data.val = value; + } + }, + }, + handles: { + "core:text-field": { + edit(p, content) { + if (this.data.val !== content) { + this.mod("set", content); + } }, - "handles": { - "core:text-field": { - "edit"(p, content) { - if (this.data.val !== content) { - this.mod("set", content); - } - }, - "escape"(p) { - this.root.focus(); - }, - }, - "core:active"(p) { - this.struct_ref("field").request_pack(p); - }, - "core:selection": { - "enter"(p) { - this.request("core:active"); - this.select_all(); - }, - }, + escape(p) { + this.root.focus(); }, - } + }, + "core:active"(p) { + this.struct_ref("field").request_pack(p); + }, + "core:selection": { + enter(p) { + this.request("core:active"); + this.select_all(); + }, + }, + }, + }; - init(data) { - this.data = data ?? ['']; - } + init(data) { + this.data = data ?? [""]; + } - struct($) { - const { - "#core:text-field": field, - } = this.$type; - return $("field", field(this.data.bclone()) - .into(this) - .name(name) - .prefix('"').suffix('"') - .color(60, 0.5, 1.2) - .style_on("inline", "code")) - .melem; - } + struct($) { + const { "#core:text-field": field } = this.$type; + return $( + "field", + field(this.data.bclone()) + .into(this) + .prefix('"') + .suffix('"') + .color(60, 0.5, 1.2) + .style_on("inline", "code"), + ).melem; + } - to_json() { - return this.data[0]; - } + to_json() { + return this.data[0]; + } - select_all() { - this.struct_ref("field").select_all(); - } -} \ No newline at end of file + select_all() { + this.struct_ref("field").select_all(); + } +} diff --git a/src/core/plugin/json/types.plugin.js b/src/core/plugin/json/types.plugin.js index 97a16f0a..afe0e00b 100644 --- a/src/core/plugin/json/types.plugin.js +++ b/src/core/plugin/json/types.plugin.js @@ -1,51 +1,46 @@ -const id = "#types:core:json" -const provides = [".types:json", ".types"] -const requires = [ - ".types:core:basic", -] +const id = "#types:core:json"; +const provides = [".types:json", ".types"]; +const requires = [".types:core:basic"]; export default class extends TTPlugin { - static id = id - static provides = provides - static requires(plugins) { - return this.req_essential(plugins, requires); - } + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } - ".core:type-loader" = [ - "type/null", - "type/boolean", - "type/string", - "type/number", - "type/object", - "type/key", - "type/array", - ] + ".core:type-loader" = [ + "type/null", + "type/boolean", + "type/string", + "type/number", + "type/object", + "type/key", + "type/array", + ]; - ".core:rule-loader" = [ - "rule/action", - "rule/type", - "rule/keymap", - ]; + ".core:rule-loader" = ["rule/action", "rule/type", "rule/keymap"]; - ".core:style-loader" = [ - "style/array", - "style/key", - "style/number", - "style/object", - ]; + ".core:style-loader" = [ + "style/array", + "style/key", + "style/number", + "style/object", + ]; - async request_insert(anchor) { - const id = await this.root.$require[".core:type-selector"].request(anchor, n => n.in(".json:") && !n.is(".json:key")); - if (id) { - const Node = this.root.$type[id]; - const nnode = Node(); - return nnode; - } else { - return null; - } + async request_insert(anchor) { + const id = await this.root.$require[".core:type-selector"].request( + anchor, + (n) => n.in(".json:") && !n.is(".json:key"), + ); + if (id) { + return this.make(id); + } else { + return null; } + } - make(id, data) { - return this.root.$type[id](data); - } + make(id, data) { + return this.root.$type[id](data); + } } diff --git a/src/core/plugin/stash/index.plugin.js b/src/core/plugin/stash/index.plugin.disabled.js similarity index 100% rename from src/core/plugin/stash/index.plugin.js rename to src/core/plugin/stash/index.plugin.disabled.js diff --git a/src/core/plugin/tee/dsl/html.plugin.js b/src/core/plugin/tee/dsl/html.plugin.js new file mode 100644 index 00000000..589998e9 --- /dev/null +++ b/src/core/plugin/tee/dsl/html.plugin.js @@ -0,0 +1,16 @@ +const id = "#types:core:tee:html"; +const provides = [".types:tee:html"]; +const requires = []; + +export default class extends TTPlugin { + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } + + ".core:tee.type" = { + "html:node": { name: "Node" }, + "html:div": { name: "Div" }, + }; +} diff --git a/src/core/plugin/tee/dsl/val/boolean.js b/src/core/plugin/tee/dsl/val/boolean.js new file mode 100644 index 00000000..340dfd1a --- /dev/null +++ b/src/core/plugin/tee/dsl/val/boolean.js @@ -0,0 +1,12 @@ +const id = "#core:tee:val:boolean"; +const extend = "#core:json:boolean"; +const provides = [".tee:val:boolean"]; +const name = Names("Boolean"); + +const Super = await TTNode.Class(extend); +export default class extends Super { + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; +} diff --git a/src/core/plugin/tee/dsl/val/number.js b/src/core/plugin/tee/dsl/val/number.js new file mode 100644 index 00000000..40d0e84f --- /dev/null +++ b/src/core/plugin/tee/dsl/val/number.js @@ -0,0 +1,12 @@ +const id = "#core:tee:val:number"; +const extend = "#core:json:number"; +const provides = [".tee:val:number"]; +const name = Names("Number"); + +const Super = await TTNode.Class(extend); +export default class extends Super { + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; +} diff --git a/src/core/plugin/tee/dsl/val/string.js b/src/core/plugin/tee/dsl/val/string.js new file mode 100644 index 00000000..99f0f03e --- /dev/null +++ b/src/core/plugin/tee/dsl/val/string.js @@ -0,0 +1,12 @@ +const id = "#core:tee:val:string"; +const extend = "#core:json:string"; +const provides = [".tee:val:string"]; +const name = Names("String"); + +const Super = await TTNode.Class(extend); +export default class extends Super { + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; +} diff --git a/src/core/plugin/tee/dsl/val/types.plugin.js b/src/core/plugin/tee/dsl/val/types.plugin.js new file mode 100644 index 00000000..18c08605 --- /dev/null +++ b/src/core/plugin/tee/dsl/val/types.plugin.js @@ -0,0 +1,13 @@ +const id = "#types:core:tee:val"; +const provides = [".types:tee:val", ".types"]; +const requires = [".types:json"]; + +export default class extends TTPlugin { + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } + + ".core:type-loader" = ["boolean", "string", "number"]; +} diff --git a/src/core/plugin/tee/index.plugin.js b/src/core/plugin/tee/index.plugin.js new file mode 100644 index 00000000..9fb45d2d --- /dev/null +++ b/src/core/plugin/tee/index.plugin.js @@ -0,0 +1,48 @@ +const id = "#core:tee"; +const provides = [".core:tee"]; +const requires = { + base: ".core:base", + rule: ".core:rule-loader", +}; + +export default class extends TTPlugin { + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } + + init() { + return () => this.load(); + } + + async load() { + const { import_type, import_rule, for_plugins_prop } = this.require.base; + const { parse_rule } = this.require.rule; + + await for_plugins_prop(`${provides.val}.type`, (plugin, types) => + Promise.all( + Object.entries(types).map(async ([tree_id, opts]) => { + const id = `#core:tee:dsl:${tree_id}`; + const extend = opts.extend + ? `#core:tee:dsl:${opts.extend}` + : "#core:tee:node"; + const provides = [`.tee:dsl:${tree_id}`]; + + const Super = await TTNode.Class(extend); + const Node = class extends Super { + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = opts.name ? Names(opts.name) : Super.name; + static tid = tree_id; + }; + if (opts.rule) { + import_rule(...parse_rule(Node.id, opts.rule)); + } + import_type(Node); + }), + ), + ); + } +} diff --git a/src/core/plugin/tee/rule/action.js b/src/core/plugin/tee/rule/action.js new file mode 100644 index 00000000..b3af2481 --- /dev/null +++ b/src/core/plugin/tee/rule/action.js @@ -0,0 +1,130 @@ +export default (plugin) => ({ + ".tee:node": { + actions: { + "core:insert-into": class extends TTAction { + static name = Names("Insert Into"); + static icon = "plus"; + static unique = true; + static async call(node) { + const nnode = await plugin.request_insert(node); + if (nnode) { + const offset = node.data.length; + node.mod("modify", offset, 0, [nnode]); + } + } + }, + "core:clear": class extends TTAction { + static name = Names("Clear"); + static icon = "eraser"; + static varify(node) { + return node.data.length > 0; + } + static call(node) { + node.mod("modify", 0, node.data.length, []); + } + }, + }, + }, + + ".tee:node > .core:selection": { + actions: { + "core:insert": class extends TTAction { + static name = Names("Insert"); + static icon = "plus"; + static unique = true; + static async call(sel, nnode) { + const node = sel.parent; + nnode ??= await plugin.request_insert(sel); + if (nnode) { + const [, offset] = sel.data_range; + node.mod("modify", offset, 0, [nnode]); + sel.set(offset, offset + 1); + } + } + }, + "core:switch": class extends TTAction { + static name = Names("Switch"); + static icon = "switch"; + static unique = true; + static varify(sel) { + return sel.data_nodes.length === 1; + } + static async call(sel) { + const node = sel.parent; + const nnode = await plugin.request_insert(sel); + if (nnode) { + const [offset] = sel.data_range; + node.mod("modify", offset, 1, [nnode]); + sel.set(offset, offset + 1); + } + } + }, + "core:select-all": class extends TTAction { + static name = Names("Select All"); + static icon = "select-all"; + static unique = true; + static call(sel) { + const node = sel.parent; + sel.set(0, node.data.length); + } + }, + "core:restruct": class extends TTAction { + static name = Names("Restruct"); + static icon = "brackets-contain"; + static varify(sel) { + return !sel.collapsed(); + } + static async call(sel) { + const node = sel.parent; + const nnode = await plugin.request_insert(sel); + if (nnode) { + const [start, end] = sel.data_range.num_sorted(); + const data = node.data.slice(start, end); + node.mod("modify", start, end - start, []); + node.mod("modify", start, 0, [nnode]); + nnode.mod("modify", 0, 0, data); + sel.set(start, start + 1); + } + } + }, + "core:extract": class extends TTAction { + static name = Names("Extract"); + static icon = "dots"; + static varify(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + return node.data.slice(start, end).some((n) => n.in(".tee:dsl:")); + } + static call(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + const data = []; + for (let i = start; i < end; i++) { + const n = node.data[i]; + if (n.in(".tee:dsl:")) { + data.push(...n.data); + n.mod("modify", 0, n.data.length, []); + } else { + data.push(n); + } + } + node.mod("modify", start, end - start, data); + sel.set(start, start + data.length); + } + }, + "core:delete": class extends TTAction { + static name = Names("Delete"); + static icon = "trash"; + static varify(sel) { + return !sel.collapsed(); + } + static call(sel) { + const node = sel.parent; + const [start, end] = sel.data_range.num_sorted(); + node.mod("modify", start, end - start, []); + sel.set(start, start); + } + }, + }, + }, +}); diff --git a/src/core/plugin/tee/rule/keymap.js b/src/core/plugin/tee/rule/keymap.js new file mode 100644 index 00000000..45bc14e9 --- /dev/null +++ b/src/core/plugin/tee/rule/keymap.js @@ -0,0 +1 @@ +export default (plugin) => ({}); diff --git a/src/core/plugin/tee/style/node.css b/src/core/plugin/tee/style/node.css new file mode 100644 index 00000000..48c1b45d --- /dev/null +++ b/src/core/plugin/tee/style/node.css @@ -0,0 +1,11 @@ +.core-tee-node { + align-self: start; + display: flex; + flex-direction: column; +} + +.core-tee-node-flex { + margin-left: 2ch; + display: flex; + flex-direction: column; +} diff --git a/src/core/plugin/tee/trans.plugin.js b/src/core/plugin/tee/trans.plugin.js new file mode 100644 index 00000000..b4257b67 --- /dev/null +++ b/src/core/plugin/tee/trans.plugin.js @@ -0,0 +1,15 @@ +const id = "#trans:core:tee"; +const provides = [".trans:core:tee", ".trans"]; +const requires = {}; + +export default class extends TTPlugin { + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } + + init() { + Names("Tee", { "zh-CN": "Tee" }); + } +} diff --git a/src/core/plugin/tee/type/node.js b/src/core/plugin/tee/type/node.js new file mode 100644 index 00000000..ffc312e2 --- /dev/null +++ b/src/core/plugin/tee/type/node.js @@ -0,0 +1,157 @@ +const id = "#core:tee:node"; +const extend = null; +const provides = [".tee:node"]; +const name = Names("Node"); + +const Super = await TTNode.Class(extend); +export default class extends Super { + static id = id; + static provides = provides; + static uses = [id, ...provides, ...Super.uses]; + static name = name; + + static rule = { + modifiers: { + modify: class extends TTModer.Map { + modify(node, offset, delete_count, inserts) { + super.modify(node); + const deletes = node.data.modify(offset, delete_count, inserts); + this.data_src = [offset, inserts.length, deletes]; + } + }, + move: class extends TTModer.Map { + modify(node, offset, count, delta) { + super.modify(node); + this.data_src = [offset + delta, count, -delta]; + node.data.move(offset, count, delta); + } + }, + }, + "able.core:layout.select": true, + "handles.core:layout": { + "get-selection"(p, anchor_node, focus_node) { + let anchor = this.data.indexOf(anchor_node); + let focus = this.data.indexOf(focus_node); + if (anchor <= focus) { + focus++; + } else { + anchor++; + } + return this.$type["#core:selection"]({ + scope: this, + anchor, + focus, + }); + }, + }, + "handles.core:selection": { + enter(p, sel) { + sel.data_scope.val = this; + sel.set(0, Math.min(this.data.length, 1)); + }, + select(p, sel, node) { + sel.data_scope.val = this; + const offset = this.data.indexOf(node); + sel.set(offset, offset + 1); + }, + resolve(p, { anchor, focus }) { + if (anchor === focus) { + if (this.data.length) { + const pnode = this.data[anchor - 1]; + if (pnode) { + const rect = pnode.melem.rect; + return [ + "cursor", + pnode, + { dir: "y", x: 0, y: rect.height, size: rect.width }, + ]; + } else { + const nnode = this.data[anchor]; + const rect = nnode.melem.rect; + return [ + "cursor", + nnode, + { dir: "y", x: 0, y: 0, size: rect.width }, + ]; + } + } else { + const { width: w, height: h } = this.melem.rect; + return ["cursor", this, { dir: "y", x: 18, y: h, size: w - 18 }]; + } + } else { + return ["range", this.data.slice(...[anchor, focus].num_sorted())]; + } + }, + dir(p) { + return { top: true, bottom: true }; + }, + collapse(p, dir, { anchor, focus }) { + if (["top", "left"].includes(dir)) { + return ((p) => [p, p])(Math.min(anchor, focus)); + } + if (["bottom", "right"].includes(dir)) { + return ((p) => [p, p])(Math.max(anchor, focus)); + } + }, + collapsed(p, dir, { anchor, focus }) { + return anchor === focus; + }, + side(p, dir, pos) { + if (["top"].includes(dir)) { + return 0; + } + if (["bottom"].includes(dir)) { + return this.data.length; + } + }, + move(p, dir, pos) { + switch (dir) { + case "top": + return pos - 1; + case "bottom": + return pos + 1; + } + }, + varify(p, pos) { + return 0 <= pos && pos <= this.data.length; + }, + }, + }; + + init(data) { + this.data = (data ?? []).guard( + null, + (n) => n.into(this), + (n) => n.outof(), + ); + } + + struct() { + const { "#core:frame": frame } = this.$type; + return ME.div.class("core-tee-node")( + frame([this.constructor.name.get()]) + .style_on("inline", "code") + .into(this) + .color(0, 0).melem, + ME.div + .class("core-tee-node-flex") + .$inner(this.data.bflat().bmap((node) => node.melem))(), + ); + } + + to_json() { + return this.data.map((node) => node.to_json()); + } + + get(index) { + return this.data[index]; + } + + index(node) { + return this.data.indexOf(node); + } + + has(node) { + return this.data.includes(node); + } +} diff --git a/src/core/plugin/tee/types.plugin.js b/src/core/plugin/tee/types.plugin.js new file mode 100644 index 00000000..2ff80316 --- /dev/null +++ b/src/core/plugin/tee/types.plugin.js @@ -0,0 +1,33 @@ +const id = "#types:core:tee"; +const provides = [".types:tee", ".types"]; +const requires = []; + +export default class extends TTPlugin { + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } + + ".core:type-loader" = ["type/node"]; + + ".core:rule-loader" = ["rule/action", "rule/keymap"]; + + ".core:style-loader" = ["style/node"]; + + async request_insert(anchor) { + const id = await this.root.$require[".core:type-selector"].request( + anchor, + (n) => n.in(".tee:dsl:") || n.in(".tee:val:"), + ); + if (id) { + return this.make(id); + } else { + return null; + } + } + + make(id, data) { + return this.root.$type[id](data); + } +} diff --git a/src/core/plugin/type-selector/index.plugin.js b/src/core/plugin/type-selector/index.plugin.js index b908ba80..6d4732be 100644 --- a/src/core/plugin/type-selector/index.plugin.js +++ b/src/core/plugin/type-selector/index.plugin.js @@ -1,35 +1,35 @@ -const id = "#core:type-selector" -const provides = [".core:type-selector"] +const id = "#core:type-selector"; +const provides = [".core:type-selector"]; const requires = { - commander: ".core:commander", -} + commander: ".core:commander", +}; export default class extends TTPlugin { - static id = id - static provides = provides - static requires(plugins) { - return this.req_essential(plugins, requires); - } + static id = id; + static provides = provides; + static requires(plugins) { + return this.req_essential(plugins, requires); + } - async request(anchor, filter, opts) { - const types = this.root.types.filter(filter); - const res = await this.require.commander.request( - to_melem(anchor), - types.map(Type => ({ - id: Type.id, - label: Type.name.get(), - detail: Type.id, - path: Type.id + Type.provides.join(''), - })), - { - anchor_x: 1, - anchor_y: 0, - ...opts, - }, - ); - this.root.focus(); - return res ? res.id : null; - } + async request(anchor, filter, opts) { + const types = filter ? this.root.types.filter(filter) : this.root.types; + const res = await this.require.commander.request( + to_melem(anchor), + types.map((Type) => ({ + id: Type.id, + label: Type.name.get(), + detail: Type.id, + path: Type.id + Type.provides.join(""), + })), + { + anchor_x: 1, + anchor_y: 0, + ...opts, + }, + ); + this.root.focus(); + return res ? res.id : null; + } } -const to_melem = node => node instanceof TTNode ? node.melem : node; \ No newline at end of file +const to_melem = (node) => (node instanceof TTNode ? node.melem : node); diff --git a/src/main.js b/src/main.js index 344f9378..8b26a683 100644 --- a/src/main.js +++ b/src/main.js @@ -10,82 +10,95 @@ const WINDOW_MIN_WIDTH = 500; const WINDOW_MIN_HEIGHT = 200; const App = new (class { - window = null; + window = null; - init() { - this.window = new BrowserWindow({ - width: 700, - height: 500, - minWidth: WINDOW_MIN_WIDTH, - minHeight: WINDOW_MIN_HEIGHT, - webPreferences: { - contextIsolation: false, - nodeIntegration: true, - }, - autoHideMenuBar: true, - show: false, - frame: false, - }); - this.window.loadFile(join(__dirname, "app.html")); - // this.window.loadURL("http://127.0.0.1:5500/src/app.html"); - this.window.setMenuBarVisibility(false); + init() { + this.window = new BrowserWindow({ + width: 700, + height: 500, + minWidth: WINDOW_MIN_WIDTH, + minHeight: WINDOW_MIN_HEIGHT, + webPreferences: { + contextIsolation: false, + nodeIntegration: true, + }, + autoHideMenuBar: true, + // show: false, + frame: false, + }); + this.window.loadFile(join(__dirname, "app.html")); + // this.window.loadURL("http://127.0.0.1:5500/src/app.html"); + this.window.setMenuBarVisibility(false); - ipcMain.handle("show_window", () => this.window.show()); - ipcMain.handle("minimize_window", () => this.window.minimize()); - ipcMain.handle("toggle_maximize_window", () => this.window.isMaximized() ? this.window.unmaximize() : this.window.maximize()); - ipcMain.handle("resize_window_by", (_, dw, dh, initial = false) => { - if (this.window.isMaximized()) return; - const {x: px, y: py, width: pw, height: ph} = this.window.getBounds(); - dw = Math.max(dw, WINDOW_MIN_WIDTH - pw); - if (initial) { - this.window.setBounds({x: px - dw / 2, y: py - dh / 2, width: pw + dw, height: ph + dh}, false); - } else { - this.window.setBounds({x: px - dw, width: pw + dw, height: ph + dh}, true); - } - }); - ipcMain.handle("exit", () => app.exit()); + ipcMain.handle("show_window", () => this.window.show()); + ipcMain.handle("minimize_window", () => this.window.minimize()); + ipcMain.handle("toggle_maximize_window", () => + this.window.isMaximized() + ? this.window.unmaximize() + : this.window.maximize(), + ); + ipcMain.handle("resize_window_by", (_, dw, dh, initial = false) => { + if (this.window.isMaximized()) return; + const { x: px, y: py, width: pw, height: ph } = this.window.getBounds(); + dw = Math.max(dw, WINDOW_MIN_WIDTH - pw); + if (initial) { + this.window.setBounds( + { x: px - dw / 2, y: py - dh / 2, width: pw + dw, height: ph + dh }, + false, + ); + } else { + this.window.setBounds( + { x: px - dw, width: pw + dw, height: ph + dh }, + true, + ); + } + }); + ipcMain.handle("exit", () => app.exit()); - ipcMain.handle("click", ({sender}, opts) => { - sender.sendInputEvent({type: "mouseDown", ...opts}); - sender.sendInputEvent({type: "mouseUp", ...opts}); - }); + ipcMain.handle("click", ({ sender }, opts) => { + sender.sendInputEvent({ type: "mouseDown", ...opts }); + sender.sendInputEvent({ type: "mouseUp", ...opts }); + }); - ipcMain.handle("get_arg", () => { - const argv = [...process.argv]; - if (!app.isPackaged) argv.shift() - const finish = path => path ? resolve(path) : path ?? null; - return finish(argv[1]); - }); - ipcMain.handle("open_file", async (_, file_path) => { - const data = fs.readFileSync(file_path, 'utf-8'); - return { file_path, data }; - }); - ipcMain.handle("open_file_dialog", async (_, current_file_path) => { - const { canceled, filePaths } = await dialog.showOpenDialog(this.window, { - defaultPath: current_file_path ? dirname(current_file_path) : undefined, - properties: ['openFile'] - }); - if (!canceled && filePaths.length > 0) { - const file_path = filePaths[0]; - const data = fs.readFileSync(file_path, 'utf-8'); - return { file_path, data }; - } - return null; - }); - ipcMain.handle("save_file", async (_, file_path, data) => { - fs.writeFileSync(file_path, data); - }); - ipcMain.handle("save_file_as", async (_, file_path, data) => { - const { canceled, filePath: save_path } = await dialog.showSaveDialog(this.window, { - defaultPath: file_path ? file_path : undefined - }); - if (!canceled && save_path) { - fs.writeFileSync(save_path, data); - return save_path; - } - return null; - }); - } + ipcMain.handle("get_arg", () => { + const argv = [...process.argv]; + if (!app.isPackaged) argv.shift(); + const finish = (path) => (path ? resolve(path) : (path ?? null)); + return finish(argv[1]); + }); + ipcMain.handle("open_file", async (_, file_path) => { + const data = fs.readFileSync(file_path, "utf-8"); + return { file_path, data }; + }); + ipcMain.handle("open_file_dialog", async (_, current_file_path) => { + const { canceled, filePaths } = await dialog.showOpenDialog(this.window, { + defaultPath: current_file_path ? dirname(current_file_path) : undefined, + properties: ["openFile"], + }); + if (!canceled && filePaths.length > 0) { + const file_path = filePaths[0]; + const data = fs.readFileSync(file_path, "utf-8"); + return { file_path, data }; + } + return null; + }); + ipcMain.handle("save_file", async (_, file_path, data) => { + fs.writeFileSync(file_path, data); + }); + ipcMain.handle("save_file_as", async (_, file_path, data) => { + const { canceled, filePath: save_path } = await dialog.showSaveDialog( + this.window, + { + defaultPath: file_path ? file_path : undefined, + }, + ); + if (!canceled && save_path) { + fs.writeFileSync(save_path, data); + return save_path; + } + return null; + }); + } })(); app.once("ready", () => App.init()); diff --git a/src/plugins.cfg b/src/plugins.cfg index 37b537fc..a240f8af 100644 --- a/src/plugins.cfg +++ b/src/plugins.cfg @@ -1,20 +1,24 @@ -./core/base/style-loader.plugin.js -./core/base/rule-loader.plugin.js -./core/base/view-manager.plugin.js -./core/base/init-manager.plugin.js -./core/base/type-loader.plugin.js -./core/plugin/stash/index.plugin.js -./core/plugin/json/types.plugin.js ./core/plugin/json/trans.plugin.js -./core/plugin/timeline/index.plugin.js -./core/plugin/type-selector/index.plugin.js -./core/plugin/basic/shortcut.plugin.js -./core/plugin/basic/types.plugin.js -./core/plugin/basic/commander.plugin.js +./core/plugin/json/types.plugin.js ./core/plugin/basic/icon-loader.plugin.js -./core/plugin/basic/layout.plugin.js -./core/plugin/basic/popup.plugin.js ./core/plugin/basic/trans.plugin.js -./core/plugin/file/index.plugin.js -./core/plugin/context-menu/trans.plugin.js +./core/plugin/basic/popup.plugin.js +./core/plugin/basic/commander.plugin.js +./core/plugin/basic/shortcut.plugin.js +./core/plugin/basic/layout.plugin.js +./core/plugin/basic/types.plugin.js +./core/plugin/timeline/index.plugin.js +./core/plugin/type-selector/index.plugin.js ./core/plugin/context-menu/index.plugin.js +./core/plugin/context-menu/trans.plugin.js +./core/plugin/file/index.plugin.js +./core/plugin/tee/types.plugin.js +./core/plugin/tee/trans.plugin.js +./core/plugin/tee/dsl/html.plugin.js +./core/plugin/tee/dsl/val/types.plugin.js +./core/plugin/tee/index.plugin.js +./core/base/init-manager.plugin.js +./core/base/style-loader.plugin.js +./core/base/view-manager.plugin.js +./core/base/type-loader.plugin.js +./core/base/rule-loader.plugin.js