diff --git a/frame.js b/frame.js index 79d27a4c..5260370a 100644 --- a/frame.js +++ b/frame.js @@ -542,8 +542,8 @@ Volcanos(chat.ONMOTION, {_init: function(can, target) { }) }, }, - scrollHold: function(can, cb, target, scroll) { target = target || can._output - var top = target.scrollTop, left = target.scrollLeft; cb(), scroll || (target.scrollTop = top), target.scrollLeft = left + scrollHold: function(can, cb, target) { target = target || can._output + var top = target.scrollTop, left = target.scrollLeft; cb(), target.scrollLeft = left }, clearFloat: function(can) { var list = ["fieldset.input.float", "div.input.float", "div.carte.float"]; for (var i = 0; i < list.length; i++) { diff --git a/index.css b/index.css index a7b798c1..77f8aa5c 100644 --- a/index.css +++ b/index.css @@ -297,7 +297,11 @@ fieldset.plugin.inner.cmd>form.option { display:none; } fieldset.plugin.inner.cmd fieldset.xterm>form.option>div.item.text>input { width:320px; } fieldset.plan div.output table.content { height:100%; width:100%; } fieldset.xterm div.toggle { display:none; } +fieldset.xterm div.output.select { border:blue solid 1px; } fieldset.xterm.output>div.output { overflow:hidden; } +fieldset.xterm div.output { border-left:gray solid 1px; border-top:gray solid 1px; } +div.layout.flex>div.output { float:left; clear:none; } +fieldset>div.layout { clear:both; } fieldset.draw td.content { position:relative; } fieldset.draw div.output div.content svg { background-color:#1b5b738c; } fieldset.draw.spide div.output div.toggle { display:none; } diff --git a/lib/page.js b/lib/page.js index 919e6f47..0d9c7d71 100644 --- a/lib/page.js +++ b/lib/page.js @@ -21,7 +21,7 @@ Volcanos("page", { return can.page.Select(can, target, key? "select[name="+key+"],"+"input.select[type=button][name="+key+"],"+"input[name="+key+"],"+"textarea[name="+key+"]": ".args", cb) }, SelectInput: function(can, target, name, cb) { return can.page.Select(can, target, "input[name="+name+"]", cb)[0] }, - SelectChild: function(can, target, key, cb) { var i = 0; return can.page.Select(can, target, key, function(node) { if (node.parentNode == target) { return cb(node, i++) } }) }, + SelectChild: function(can, target, key, cb) { var i = 0; return can.page.Select(can, target, key, function(node) { if (node.parentNode == target) { return cb? cb(node, i++): node } }) }, SelectOne: function(can, target, key, cb) { return can.page.Select(can, target, key, function(target, index) { return index == 0 && can.base.isFunc(cb) && cb(target), target })[0] }, Select: function(can, target, key, cb, interval, cbs) { target = target || document.body return can.core.List(key == ice.PT? [target]: target.querySelectorAll(can.page.Keys(key)), cb, interval, cbs) diff --git a/panel/header.js b/panel/header.js index 4284b93b..4ea703be 100644 --- a/panel/header.js +++ b/panel/header.js @@ -6,7 +6,7 @@ Volcanos(chat.ONIMPORT, {_init: function(can, msg, target) { }) }, _state: function(can, msg, target) { can.user.isMobile || can.core.List(can.base.Obj(can.Conf(chat.STATE)||msg.Option(chat.STATE), [aaa.USERNICK, aaa.AVATAR, mdb.TIME]).reverse(), function(item) { if (item == aaa.AVATAR ) { can.user.isLocalFile || can.page.Append(can, target, [{view: [[html.ITEM, chat.STATE, item]], list: [{img: ice.SP}], onclick: function(event) { - can.onaction.carte(event, can, [can.page.Format(html.IMG, can.onexport.avatar(can), can.page.height()/2)]) + can.core.CallFunc([can.onaction, item], [event, can, item]) }}]); return } can.page.Append(can, target, [{view: [[html.ITEM, chat.STATE, item], "", (can.Conf(item)||msg.Option(item)||"").split(ice.AT)[0].slice(0, 10)], onclick: function(event) { can.core.CallFunc([can.onaction, item], [event, can, item]) @@ -95,6 +95,7 @@ Volcanos(chat.ONACTION, {_init: function(can) { var themeMedia = window.matchMed }, carte: function(event, can, list, cb, trans) { can.user.carte(event, can, can.onaction, list, cb, null, trans) }, share: function(event, can, args) { can.user.share(can, can.request(event), [ctx.ACTION, chat.SHARE].concat(args||[])) }, + avatar: function(event, can) { can.onaction.carte(event, can, [can.page.Format(html.IMG, can.onexport.avatar(can), can.page.height()/2)]) }, usernick: function(event, can) { can.user.mod.isPod || can.user.isExtension || can.user.isLocalFile || can.onaction.carte(event, can, can.onaction._menus) }, shareuser: function(event, can) { can.user.share(can, can.request(event), [ctx.ACTION, chat.SHARE, mdb.TYPE, aaa.LOGIN]) }, toimage: function(event, can) { can.onmotion.clearCarte(can), can.user.toimage(can, can.user.title(), can._target.parentNode) }, diff --git a/plugin/local/code/inner.js b/plugin/local/code/inner.js index 912d1efb..89999be1 100644 --- a/plugin/local/code/inner.js +++ b/plugin/local/code/inner.js @@ -63,10 +63,9 @@ Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb, target) { can.onmotion.cl can.user.isMobile || can.page.Append(can, can.ui.tabs, [{view: mdb.TIME, _init: function(target) { can.core.Timer({interval: 100}, function() { can.page.Modify(can, target, can.user.time(can, null, "%H:%M:%S %w")) }) can.onappend.figure(can, {action: "date", _hold: true}, target, function(sub, value) {}) - }}]), can.user.info.avatar && can.page.Append(can, can.ui.tabs, [{view: aaa.AVATAR, list: [{img: can.user.info.avatar}]}]) - can.page.Append(can, can.ui.tabs, [{view: ["usernick", "", can.user.info.usernick], onclick: function(event) { - can._root.Header.onaction.usernick(event, can._root.Header) }}]) + can.page.Append(can, can.ui.tabs, [{view: aaa.AVATAR, list: [{img: can.user.info.avatar}], onclick: function(event) { can._root.Header.onaction.avatar(event, can._root.Header) }}]) + can.page.Append(can, can.ui.tabs, [{view: [aaa.USERNICK, "", can.user.info.usernick], onclick: function(event) { can._root.Header.onaction.usernick(event, can._root.Header) }}]) }, __tabPath: function(can, skip) { can.onmotion.clear(can, can.ui.path) can.onimport._tabPath(can, ice.PS, nfs.PATH, can.base.Path(can.Option(nfs.PATH), can.Option(nfs.FILE)), function(ls) { diff --git a/plugin/local/code/xterm.js b/plugin/local/code/xterm.js index 781b6db8..e6d6005b 100644 --- a/plugin/local/code/xterm.js +++ b/plugin/local/code/xterm.js @@ -1,52 +1,92 @@ -Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb) { can.onmotion.clear(can), can.onlayout._init(can) - can.page.requireModules(can, ["xterm/css/xterm.css", "xterm", "xterm-addon-fit", "xterm-addon-web-links"], function() { - can._delay_cmds = function() { delete(can._delay_cmds), item.text && can.core.Next(item.text.split(ice.NL), function(item, next) { - can.onimport._input(can, item+ice.NL), can.onmotion.delay(can, next) - }) } - var item = msg.TableDetail(); item.hash = item.hash||can.Option(mdb.HASH), item.text && can.onmotion.delay(can, function() { can._delay_cmds && can._delay_cmds() }, 1000) - can.onimport._connect(can, item), can.onappend.tools(can, msg, function(sub) { - sub.onexport.record = function(_, value, key, line) { can.onimport._input(can, value+ice.NL) } - }), msg.Option(ice.MSG_TOOLKIT, ""), can.base.isFunc(cb) && cb(msg), can.onappend._status(can) - can.sup._save && can._current.write(can.sup._save.replaceAll(ice.NL, "\r\n")), can.sup._save = "" - can.sup._listen || can.onengine.listen(can, chat.ONTHEMECHANGE, function() { can = can.core.Value(can.sup, chat._OUTPUTS_CURRENT) - can._current.selectAll(), can.sup._save = can.base.trimSuffix(can._current.getSelection(), ice.NL), can.Update() - }), can.sup._listen = true - can.sup.onexport.link = function() { return can.misc.MergePodCmd(can, {cmd: web.CODE_XTERM, hash: item.hash, style: html.OUTPUT}) } - }) +Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb) { can.page.requireModules(can, ["xterm/css/xterm.css", "xterm", "xterm-addon-fit", "xterm-addon-web-links"], function() { + var item = msg.TableDetail(); item.hash = item.hash||can.Option(mdb.HASH), can.onmotion.clear(can), can.base.isFunc(cb) && cb(msg), can.onappend._status(can) + if (item.type == html.LAYOUT) { can.onimport._layout(can, item) } else { can.onimport._connect(can, item, can._output) } can.onimport.layout(can) + can.sup.onexport.link = function() { return can.misc.MergePodCmd(can, {cmd: web.CODE_XTERM, hash: item.hash, style: html.OUTPUT}) } + }) }, + _layout: function(can, item) { + function show(list, target, root, tabs) { root = root||target, can.core.List(list, function(item) { + if (item.type.indexOf(html.LAYOUT) == 0) { + show(item.list, can.page.Append(can, target, [item.type])._target, root, tabs)._root = root + } else { + can.onimport._connect(can, item, can.page.Append(can, target, [html.OUTPUT])._target, tabs)._output._root = root + } + }); return target } + var main, output = can._output; can.core.List(can.base.Obj(item.text), function(item) { + if (item.type.indexOf(html.LAYOUT) == 0) { var tabs = can.onimport._tabs(can, item, {}) + var target = show(item.list, can.page.insertBefore(can, [item.type], can._status), null, tabs); target._tabs = tabs, tabs._output = target + if (item.type.indexOf(html.HIDE) == -1) { main = target } + } else { + main = main||can.onimport._connect(can, item, output||can.page.insertBefore(can, [html.OUTPUT], can._status))._output, output = null + } + }), can.onaction.select(can, main) }, - _connect: function(can, item) { var term = new Terminal({tabStopWidth: 4, cursorBlink: true, theme: can.onimport._theme(can, item)}); term.loadAddon(new WebLinksAddon.WebLinksAddon()) - can.page.style(can, can._output, html.BACKGROUND_COLOR, term._publicOptions.theme.background||cli.BLACK) + _tabs: function(can, item, output) { if (output != output._root && output._root) { return output._tabs = output._root._tabs } + var tabs = can.page.Append(can, can._action, [{view: [html.TABS, "", item.name||item.type||item.hash], onclick: function() { + can.onaction.select(can, tabs._output) + }}])._target; return tabs._output = output, output._tabs = tabs + }, + _theme: function(can, item) { return can.base.Obj(item.theme)||(can.getHeaderTheme() == html.LIGHT? {background: cli.WHITE, foreground: cli.BLACK, cursor: cli.BLUE}: {}) }, + _connect: function(can, item, output, tabs, text) { var term = new Terminal({tabStopWidth: 4, cursorBlink: true, theme: can.onimport._theme(can, item)}) var fitAddon = new FitAddon.FitAddon(); term.loadAddon(fitAddon), term._fit = fitAddon, can.onmotion.delay(can, function() { fitAddon.fit() }) term.onTitleChange(function(title) { can.onexport.title(can, title) }), can.onexport.title(can, item.name) - term.onResize(function(size) { can.onimport._resize(can, size) }) - term.onData(function(data) { can.onimport._input(can, data) }) - term.onCursorMove(function() { can.onexport.term(can) }) - can._current = term, term._item = item, term.open(can._output), term.focus() + term.onResize(function(size) { can.onimport._resize(can, term, size) }) + term.onData(function(data) { can.onimport._input(can, term, data) }) + term.onCursorMove(function() { can.onexport.term(can, term) }) + term.loadAddon(new WebLinksAddon.WebLinksAddon()) + term._item = item, term._output = output, output._term = term, output._tabs || (tabs? (output._tabs = tabs): can.onimport._tabs(can, item, output)) + term.open(output), term.focus(), text && term.write(text.replaceAll(ice.NL, "\r\n")) + can.onengine.listen(can, chat.ONTHEMECHANGE, function() { can = can.core.Value(can.sup, chat._OUTPUTS_CURRENT) + term.selectAll(), can.onimport._connect(can, item, output, tabs, can.base.trimSuffix(term.getSelection(), ice.NL)) + }), can.page.style(can, output, html.BACKGROUND_COLOR, term._publicOptions.theme.background||cli.BLACK) + output.onclick = function() { can.onmotion.select(can, can._fields, html.DIV_OUTPUT, can._output = output), can.onexport.term(can, term) } + return can.db = can.db||{}, can.db[item.hash] = term }, - _resize: function(can, size) { // can.page.style(can, can._output, html.HEIGHT, "", html.WIDTH, "") - can.runAction(can.request({}, size, can._current._item), "resize", [], function() { can.onexport.term(can) }) + _resize: function(can, term, size) { can.runAction(can.request({}, size, term._item), web.RESIZE, [], function() { can.onexport.term(can, term) }) }, + _input: function(can, term, data) { can._output = term._output, can.runAction(can.request({}, term._item), web.INPUT, [btoa(data)], function() {}) }, + grow: function(can, msg) { var arg = msg.detail.slice(1), term = can.db[arg[0]]; arg[1] == "~~~end~~~"? can.onaction.delete(can, term._output): term.write(arg[1]) }, + layout: function(can) { function show(target, height, width) { var list = can.page.SelectChild(can, target, can.page.Keys(html.DIV_OUTPUT, html.DIV_LAYOUT)) + var h = height/list.length, w = width; if (can.page.ClassList.has(can, target, html.FLEX)) { h = height, w = width/list.length } if (target == can._fields) { h = height, w = width } + can.core.List(list, function(target) { can.page.style(can, target, html.HEIGHT, h, html.WIDTH, w), can.page.ClassList.has(can, target, html.LAYOUT)? show(target, h, w): target._term && target._term._fit.fit() }) + } show(can._fields, can.ConfHeight(), can.ConfWidth()) }, +}) +Volcanos(chat.ONACTION, {list: ["+", "-", "/", "sess"], + "+": function(event, can) { can.Update(event, [ctx.ACTION, mdb.CREATE], function(msg) { can.Update(event, [msg.Result()], function(msg) { + can.onaction.select(can, can._output = can.page.insertBefore(can, [html.OUTPUT], can._status)), can.onimport._init(can, msg) + }) }) }, + "-": function(event, can) { can.onaction.split(event, can, html.FLOW) }, + "/": function(event, can) { can.onaction.split(event, can, html.FLEX) }, + split: function(event, can, button) { can.Update(event, [ctx.ACTION, mdb.CREATE], function(msg) { can.Update(event, [msg.Result()], function(msg) { + if (can.page.ClassList.has(can, can._output.parentNode, button)) { var layout = can._output.parentNode } else { + var layout = can.page.insertBefore(can, [{view: [[html.LAYOUT, button]]}], can._output); layout.appendChild(can._output) + } + var root = can._output._root||layout, tabs = can._output._tabs; tabs._output = root, root._tabs = tabs + can._output._root = root, can._output = can.page.insertBefore(can, [html.OUTPUT], can._output.nextSibling, layout) + can._output._root = root, can._output._tabs = tabs, can.onimport._init(can, msg) + }) }) }, + delete: function(can, output) { + if (output == can.sup._output) { + can.onmotion.clear(can, output) + } else { + while (output && output.parentNode.children.length == 1) { output = output.parendNode } + can.page.Remove(can, output) + } can.onimport.layout(can) }, - _input: function(can, data) { can._current && can.runAction(can.request({}, can._current._item), "input", [btoa(data)], function() {}) }, - _theme: function(can, item) { return can.base.Obj(item.theme)||(can.getHeaderTheme() == html.LIGHT? {background: cli.WHITE, foreground: cli.BLACK, cursor: cli.BLUE}: {}) }, - grow: function(can, msg, _arg) { can._current.write(_arg), can._delay_cmds && can._delay_cmds() }, + select: function(can, output) { can.page.SelectChild(can, can._fields, can.page.Keys(html.DIV_OUTPUT, html.DIV_LAYOUT), function(target) { can.onmotion.hidden(can, target, target == output || target == output._root) + can.onmotion.delay(can, function() { can.page.Select(can, target, can.page.Keys(html.DIV_OUTPUT, html.DIV_LAYOUT), function(target) { target._term && target._term._fit.fit() }) }) + }) }, + sess: function(can) { can.runAction({}, mdb.CREATE, [mdb.TYPE, html.LAYOUT, mdb.TEXT, can.base.Format(can.onexport.sess(can))], function() {}, true) }, }) -Volcanos(chat.ONLAYOUT, {_init: function(can) { - can.page.style(can, can._output, html.HEIGHT, can.ConfHeight(), html.WIDTH, can.ConfWidth(), html.MAX_WIDTH, "") - can._current && can._current._fit.fit(), can.onexport.term(can) +Volcanos(chat.ONEXPORT, {list: [mdb.TIME, mdb.HASH, mdb.TYPE, mdb.NAME, "rows", "cols", "cursorY", "cursorX"], + term: function(can, term) { item = term._item + can.core.List(can.onexport.list, function(key) { can.Status(key, can.base.getValid(item[key], term[key], term.buffer.active[key], "")+"") }) + can.core.List([mdb.HASH, mdb.TYPE, mdb.NAME], function(key) { can.Status(key, item[key]||"") }) }, - cmd: function(can) { can._current && can.onexport.title(can, can._current._item.name), can.ConfWidth(can.ConfWidth()-10) }, -}) -Volcanos(chat.ONACTION, { - refresh: function(event, can, button) { can.onlayout._init(can), can._current.focus() }, - "波浪线": function(event, can, button) { can.onimport._input(can, "~"), can._current.focus() }, - "反引号": function(event, can, button) { can.onimport._input(can, "`"), can._current.focus() }, -}) -Volcanos(chat.ONEXPORT, {list: [mdb.TIME, mdb.TYPE], -// Volcanos(chat.ONEXPORT, {list: [mdb.TIME, mdb.TYPE, mdb.NAME, "rows", "cols", "cursorY", "cursorX"], - term: function(can) { var term = can._current||{}, item = term._item; if (!item) { return } - can.core.List(can.onexport.list, function(key) { - can.Status(key, can.base.getValid(item[key], term[key], term.buffer.active[key], "")+"") - }), can.Status(mdb.TYPE, item[mdb.TYPE]||""), can.Status(mdb.NAME, item[mdb.NAME]||"") - }, - title: function(can, title) { can.sup.onexport.title(can.sup, title), can.Status(mdb.NAME, title) }, + sess: function(can) { return can.page.Select(can, can._action, html.DIV_TABS, function(target) { function show(target) { + if (can.page.ClassList.has(can, target, html.LAYOUT)) { + return {type: target.className, name: target._tabs? target._tabs.innerText: "", list: can.page.SelectChild(can, target, can.page.Keys(html.DIV_OUTPUT, html.DIV_LAYOUT), function(target) { return show(target) })} + } else { + var item = target._term._item; return {type: item.type, name: target._tabs.innerText, text: item.text, hash: item.hash} + } + } return show(target._output) }) }, + title: function(can, title) { can.page.Modify(can, can._output._tabs, title), can.Status(mdb.NAME, title), can.sup.onexport.title(can.sup, title) }, }) diff --git a/proto.js b/proto.js index 4eb0e136..ea7903dc 100644 --- a/proto.js +++ b/proto.js @@ -79,6 +79,7 @@ var web = {CHAT: "chat", GET: "GET", PUT: "PUT", POST: "POST", DELETE: "DELETE", Accept: "Accept", ContentType: "Content-Type", ContentJSON: "application/json", ContentFORM: "application/x-www-form-urlencoded", VIDEO_WEBM: "video/webm", + INPUT: "input", CODE_GIT_STATUS: "web.code.git.status", CODE_XTERM: "web.code.xterm", @@ -236,6 +237,7 @@ var html = {PLUGIN_MARGIN: 10, ACTION_HEIGHT: 32, ACTION_MARGIN: 200, DIV_CONTENT: "div.content", TABLE_CONTENT: "table.content", TABLE_LAYOUT: "table.layout", DIV_TOGGLE: "div.toggle", DIV_LAYOUT_HEAD: "div.layout.head", DIV_LAYOUT_FOOT: "div.layout.foot", DIV_LAYOUT_LEFT: "div.layout.left", DIV_FLOAT: "div.float", DIV_TOAST: "div.toast", DIV_CARTE: "div.carte", + DIV_LAYOUT: "div.layout", DIV_EXPAND: "div.expand", } var lang = {