1
0
mirror of https://shylinux.com/x/volcanos synced 2025-04-25 16:58:06 +08:00

opt toast

This commit is contained in:
IT 老营长 @云轩领航-创始人 2024-02-16 16:05:44 +08:00
parent 996fc59e3f
commit 1bb69d83ef
5 changed files with 46 additions and 43 deletions

View File

@ -31,7 +31,8 @@ Volcanos(chat.ONENGINE, {_init: function(can, meta, list, cb, target) {
can.user.info.sessid && msg.Option(ice.MSG_SESSID, can.user.info.sessid) can.user.info.sessid && msg.Option(ice.MSG_SESSID, can.user.info.sessid)
names = can.base.MergeURL(names, ice.MSG_INDEX, sub.ConfIndex()), can.page.exportValue(sub, msg) names = can.base.MergeURL(names, ice.MSG_INDEX, sub.ConfIndex()), can.page.exportValue(sub, msg)
can.onengine.signal(panel, chat.ONREMOTE, can.request({}, {_follow: panel._follow, _msg: msg, _cmds: cmds, names: names})) can.onengine.signal(panel, chat.ONREMOTE, can.request({}, {_follow: panel._follow, _msg: msg, _cmds: cmds, names: names}))
can.misc.Run(event, can, {names: names}, cmds, function(msg) { toast && can.user.toastSuccess(msg._can), toast && toast.close && toast.close(), toast = true can.misc.Run(event, can, {names: names}, cmds, function(msg) {
toast && can.user.toastSuccess(msg._can, msg.Option(ctx.ACTION)+lex.SP+ice.SUCCESS), toast && toast.close && toast.close(), toast = true
can.base.isFunc(cb) && cb(msg), Volcanos.meta.pack[can.core.Keys(panel._name, cmds.join(mdb.FS))] = msg can.base.isFunc(cb) && cb(msg), Volcanos.meta.pack[can.core.Keys(panel._name, cmds.join(mdb.FS))] = msg
}) })
}, },

View File

@ -112,10 +112,10 @@ table.content.detail td { padding:var(--table-padding) 0; }
table.content.detail td:first-child { text-align:center; } table.content.detail td:first-child { text-align:center; }
table.content.detail td i { display:none; } table.content.detail td i { display:none; }
table.content:not(.detail) td input.icons { display:none; } table.content:not(.detail) td input.icons { display:none; }
table.content.checkbox th:first-child { text-align:center; padding:var(--table-padding); position:sticky; left:2px; z-index:1; } table.content.checkbox th:first-child { text-align:center; padding:var(--table-padding) var(--input-padding); position:sticky; left:2px; z-index:1; }
table.content.checkbox td:first-child { text-align:center; position:sticky; left:2px; } table.content.checkbox td:first-child { text-align:center; padding:var(--table-padding) var(--input-padding); position:sticky; left:2px; }
table.content.action th:last-child { position:sticky; right:2px; } table.content.action th:last-child { text-align:center; padding:var(--table-padding) var(--input-padding); position:sticky; right:2px; }
table.content.action td:last-child { text-align:center; position:sticky; right:2px; } table.content.action td:last-child { text-align:center; padding:var(--table-padding) var(--input-padding); position:sticky; right:2px; }
table.content input:not(:last-child) { margin-right:var(--button-margin); } table.content input:not(:last-child) { margin-right:var(--button-margin); }
body:not(.mobile) fieldset.Action:not(.tabview):not(.horizon):not(.grid) fieldset.plugin:not(.float):not(.full):not(.cmd)>div.output>table.content td i { display:none; } body:not(.mobile) fieldset.Action:not(.tabview):not(.horizon):not(.grid) fieldset.plugin:not(.float):not(.full):not(.cmd)>div.output>table.content td i { display:none; }
body:not(.mobile) fieldset.Action:not(.tabview):not(.horizon):not(.grid) fieldset.plugin:not(.float):not(.full):not(.cmd)>div.output>table.content td input.icons { display:unset; } body:not(.mobile) fieldset.Action:not(.tabview):not(.horizon):not(.grid) fieldset.plugin:not(.float):not(.full):not(.cmd)>div.output>table.content td input.icons { display:unset; }
@ -211,23 +211,23 @@ fieldset.input.float table.content th { padding:var(--input-padding); }
fieldset.input.float table.content td { padding:var(--input-padding); } fieldset.input.float table.content td { padding:var(--input-padding); }
/* float */ /* float */
body div.float { padding:var(--plugin-padding); } body div.float { padding:var(--plugin-padding); }
fieldset.Action>div.toast { fieldset.Action>div.toast { height:fit-content; width:fit-content; overflow:auto;
height:fit-content; width:fit-content; position:absolute; bottom:var(--footer-height); right:0; z-index:10; position:absolute; right:var(--plugin-margin);
max-height:var(--float-height); overflow:auto; bottom:calc(var(--footer-height) + var(--action-height) + var(--plugin-margin)); z-index:11;
} }
fieldset.Action>div.toast>div.toast { body.mobile fieldset.Action>div.toast {
margin:var(--plugin-margin); position:static; bottom:calc(var(--footer-height) + var(--action-height));
transition:all 1s;
} }
body.mobile fieldset.Action>div.toast>div.toast { margin-bottom:10px; box-shadow:var(--notice-box-shadow); }
fieldset.Action>div.toast>div.toast { margin:var(--plugin-margin); position:static; transition:all 1s; }
div.toast.float div.title { color:var(--notice-bg-color); font-style:italic; white-space:pre; padding:0 var(--input-padding); max-width:300px; overflow:auto; float:left; cursor:copy; } div.toast.float div.title { color:var(--notice-bg-color); font-style:italic; white-space:pre; padding:0 var(--input-padding); max-width:300px; overflow:auto; float:left; cursor:copy; }
div.toast.float div.delete { color:var(--notice-bg-color); float:right; cursor:pointer; } div.toast.float div.close { color:var(--notice-bg-color); float:right; cursor:pointer; }
div.toast.float div.delete:hover { background-color:var(--hover-bg-color); color:var(--hover-fg-color); } div.toast.float div.close:hover { background-color:var(--hover-bg-color); color:var(--hover-fg-color); }
div.toast.float div.duration { color:var(--disable-fg-color); float:right; } div.toast.float div.duration { color:var(--disable-fg-color); float:right; }
div.toast.float div.content { color:var(--notice-bg-color); white-space:pre-line; text-align:center; padding:var(--input-padding); margin-bottom:var(--input-margin); min-height:28px; display:block; } div.toast.float div.content { color:var(--notice-bg-color); white-space:pre-line; text-align:center; padding:var(--input-padding); margin-bottom:var(--input-margin); min-height:28px; }
div.toast.float div.progress { border:var(--box-notice); margin-left:0px; height:5px; clear:both; } div.toast.float div.progress { border:var(--box-notice); margin-left:0px; height:5px; }
div.toast.float div.progress div.current { background-color:var(--progress-bg-color); height:4px; } div.toast.float div.progress div.current { background-color:var(--progress-bg-color); height:3px; }
div.toast.float div.action:not(.hide) { width:100%; display:block; display:flex; flex-direction:row-reverse; gap:10px; } div.toast.float div.action:not(.hide) { display:flex; flex-direction:row-reverse; gap:var(--button-margin); }
div.toast.float div.action>div.item { float:right; }
div.toast.float div.action>div.item i { display:none; } div.toast.float div.action>div.item i { display:none; }
div.toast.float div.action>div.item span { display:none; } div.toast.float div.action>div.item span { display:none; }
div.toast.float div.action>div.item input { padding:0 20px; } div.toast.float div.action>div.item input { padding:0 20px; }
@ -466,7 +466,7 @@ body div.carte { font-family:var(--code-font-family); }
body { font-size:var(--body-font-size); } body { font-size:var(--body-font-size); }
legend { font-size:var(--legend-font-size); line-height:30px; height:var(--action-height); } legend { font-size:var(--legend-font-size); line-height:30px; height:var(--action-height); }
select, input { font-size:var(--body-font-size); height:var(--action-height); } input::placeholder, textarea::placeholder { font-style:italic; color:var(--disable-fg-color); } select, input { font-size:var(--body-font-size); height:var(--action-height); } input::placeholder, textarea::placeholder { font-style:italic; color:var(--disable-fg-color); }
input[type=checkbox] { height:22px; width:22px; } input[type=checkbox] { height:16px; width:16px; }
textarea { font-size:var(--body-font-size); padding:var(--input-padding); height:var(--textarea-height); width:100%; outline:none; resize:vertical; } textarea { font-size:var(--body-font-size); padding:var(--input-padding); height:var(--textarea-height); width:100%; outline:none; resize:vertical; }
table.content.full { width:100%; } table.content.full { width:100%; }
table.content col.time { width:180px; } table.content col.time { width:180px; }
@ -514,6 +514,8 @@ div.item.text.url>input { width:var(--river-width); }
div.item.text.line>input { width:var(--button-width) !important; } div.item.text.line>input { width:var(--button-width) !important; }
div.item.text.limit>input { width:var(--button-width); } div.item.text.limit>input { width:var(--button-width); }
div.item.text.offend>input { width:var(--button-width); } div.item.text.offend>input { width:var(--button-width); }
div.item.text.path>input { width:var(--project-width); }
div.item.text.filter>input { width:var(--project-width); }
div.item.text.will>input { border:var(--box-notice); } div.item.text.will>input { border:var(--box-notice); }
div.item.text>i:first-child { position:absolute; left:0; padding:var(--input-padding); } div.item.text>i:first-child { position:absolute; left:0; padding:var(--input-padding); }
div.item.text>span.icon { font-size:var(--icon-font-size); padding:var(--input-padding); position:absolute; right:0; visibility:hidden; } div.item.text>span.icon { font-size:var(--icon-font-size); padding:var(--input-padding); position:absolute; right:0; visibility:hidden; }
@ -756,6 +758,7 @@ body.width6 fieldset.desktop>div.output>div.desktop>fieldset.web.chat.iframe>for
body.width2 fieldset.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:0; } body.width2 fieldset.desktop>div.output>div.desktop>fieldset>form.option>div.item:last-child { margin-right:0; }
body.mobile fieldset.word:not(.cmd)>form.option>div.item>input[name=path] { width:180px; } body.mobile fieldset.word:not(.cmd)>form.option>div.item>input[name=path] { width:180px; }
body.mobile fieldset.word fieldset.inner>form.option input[type=text] { display:none; } body.mobile fieldset.word fieldset.inner>form.option input[type=text] { display:none; }
body.mobile fieldset.vimer>form.option div.item.path input { width:var(--input-width); }
body.mobile fieldset.vimer>form.option div.item.line { display:none; } body.mobile fieldset.vimer>form.option div.item.line { display:none; }
body.mobile fieldset.Action>div.output>fieldset.plugin:not(.output):not(.float):not(.full):not(.cmd):not(:first-child) { margin:20px 0; } body.mobile fieldset.Action>div.output>fieldset.plugin:not(.output):not(.float):not(.full):not(.cmd):not(:first-child) { margin:20px 0; }
body.mobile table.content.detail { word-break:break-all; white-space:unset; } body.mobile table.content.detail { word-break:break-all; white-space:unset; }

View File

@ -80,48 +80,42 @@ Volcanos("user", {
var carte = can.user.toast(can, {content: content, title: title, action: action||[cli.CLOSE], duration: -1}) var carte = can.user.toast(can, {content: content, title: title, action: action||[cli.CLOSE], duration: -1})
can.page.style(can, carte._target, html.TOP, 200, html.BOTTOM, ""); return carte can.page.style(can, carte._target, html.TOP, 200, html.BOTTOM, ""); return carte
}, },
toastProcess: function(can, content, title, progress) { return can.user.toast(can, {content: "🕑 "+(content||ice.PROCESS), title: title, duration: -1, progress: progress, caller: 2}) }, toastProcess: function(can, content, title, progress) { return can.user.toast(can, {content: "🕑 "+(content||ice.PROCESS), title: title, duration: -1, progress: progress}) },
toastSuccess: function(can, content, title) { return can.user.toast(can, {content: "✅ "+(content||ice.SUCCESS), title: title, caller: 2}) }, toastFailure: function(can, content, title) { return can.user.toast(can, {content: "❌ "+(content||ice.FAILURE), title: title, duration: 30000}) },
toastFailure: function(can, content, title) { return can.user.toast(can, {content: "❌ "+(content||ice.FAILURE), title: title, duration: 30000, caller: 2}) }, toastSuccess: function(can, content, title) { return can.user.toast(can, {content: "✅ "+(content||ice.SUCCESS), title: title, duration: 3000}) },
toast: function(can, content, title, duration, progress, hash) { can = can._fields? can.sup: can toast: function(can, content, title, duration, progress, hash) { can = can._fields? can.sup: can
content = {"success": "✅ success", "failure": "❌ failure", "process": "🕑 process"}[content]||content
var meta = can.base.isObject(content)? content: {content: content, title: title, duration: duration, progress: progress, hash: hash} var meta = can.base.isObject(content)? content: {content: content, title: title, duration: duration, progress: progress, hash: hash}
meta.title = meta.title||can.core.Keys(can.ConfSpace(), can.ConfIndex())||can._name.split(nfs.PS).slice(-2).join(nfs.PS) meta.title = meta.title||can.core.Keys(can.ConfSpace(), can.ConfIndex())||can._name.split(nfs.PS).slice(-2).join(nfs.PS)
var width = meta.width||390; if (width < 0) { width = window.innerWidth + width } meta.action = meta.action||[""] meta.hash && (meta.title += " "+meta.hash.slice(0, 6)), meta.action = meta.action||[""]
// can.user.isChrome && (meta.hash = meta.hash||can._daemon), var width = meta.width||390; if (width < 0) { width = can.page.width() + width }
meta.hash && (meta.title += " "+meta.hash.slice(0, 6)) var ui = can.page.Append(can, can._root.Action._toast, [{view: [[chat.TOAST, meta.style, chat.FLOAT]], style: {width: width}, list: [
// var ui = can.page.Append(can, meta.hash || meta.duration < 0? can._root.Action._toast: document.body, [{view: [[chat.TOAST, meta.style, chat.FLOAT]], style: {left: (window.innerWidth-width)/2, width: width, top: can.page.height()/2}, list: [ {view: [wiki.TITLE, "", meta.title||""], title: "点击复制", onclick: function(event) { can.user.copy(event, can, meta.title) }},
var ui = can.page.Append(can, can._root.Action._toast, [{view: [[chat.TOAST, meta.style, chat.FLOAT]], style: {left: (window.innerWidth-width)/2, width: width, top: can.page.height()/2}, list: [ {view: [cli.CLOSE, "", can.page.unicode.close], title: "点击关闭", onclick: function() { action.close() }},
{text: [meta.title||"", html.DIV, html.TITLE], title: "点击复制", onclick: function(event) { can.user.copy(event, can, meta.title) }},
{view: ["delete", "", can.page.unicode.delete], title: "点击关闭", onclick: function() { action.close() }},
{view: "duration", title: "点击关闭", onclick: function() { action.close() }}, {view: "duration", title: "点击关闭", onclick: function() { action.close() }},
can.base.isObject(meta.content)? meta.content: {text: [meta.content||"", html.DIV, [nfs.CONTENT, html.FLEX]]}, can.base.isObject(meta.content)? meta.content: {view: [[nfs.CONTENT, html.FLEX], "", meta.content||""]},
html.ACTION, !can.base.isUndefined(meta.progress) && {view: "progress", style: {width: width-2*html.PLUGIN_PADDING}, list: [ html.ACTION, !can.base.isUndefined(meta.progress) && {view: "progress", style: {width: width-2*html.PLUGIN_PADDING}, list: [
{view: "current", style: {width: (meta.progress||0)*(width-2*html.PLUGIN_PADDING-2)/100}}, {view: "current", style: {width: (meta.progress||0)*(width-2*html.PLUGIN_PADDING-2)/100}},
]}, ]},
] }]); can.onengine.signal(can, chat.ONTOAST, can.request({}, {time: can.misc._time(), title: meta.title, content: meta.content})._caller(meta.caller||4)) ] }]); can.onengine.signal(can, chat.ONTOAST, can.request({}, {time: can.misc._time(), title: meta.title, content: meta.content}))
meta.action.meta && can.core.Item(meta.action.meta, function(key, cb) { cb.help && can.core.Value(meta.action.meta, ["_trans", key], cb.help) }) meta.action.meta && can.core.Item(meta.action.meta, function(key, cb) { cb.help && can.core.Value(meta.action.meta, ["_trans", key], cb.help) })
var action = can.onappend._action(can, meta.action.list? meta.action.list: meta.action, ui.action, { var action = can.onappend._action(can, meta.action.list? meta.action.list: meta.action, ui.action, {_trans: meta.action.meta? meta.action.meta._trans: {},
_trans: meta.action.meta? meta.action.meta._trans: {},
_engine: function(event, button) { can.core.CallFunc(meta.action.meta? meta.action.meta[button]: meta.action, [event, button]) }, _engine: function(event, button) { can.core.CallFunc(meta.action.meta? meta.action.meta[button]: meta.action, [event, button]) },
open: function(event) { meta.content.indexOf(ice.HTTP) == 0 && can.user.open(meta.content), meta.title.indexOf(ice.HTTP) == 0 && can.user.open(meta.title) }, open: function(event) { meta.content.indexOf(ice.HTTP) == 0 && can.user.open(meta.content), meta.title.indexOf(ice.HTTP) == 0 && can.user.open(meta.title) },
close: function(event) { action.timer.stop = true, can.page.Remove(can, ui._target) }, close: function(event) { action.timer.stop = true, can.page.Remove(can, ui._target) },
cancel: function(event) { action.timer.stop = true, can.page.Remove(can, ui._target) }, cancel: function(event) { action.timer.stop = true, can.page.Remove(can, ui._target) },
timer: can.core.Timer({interval: 100, length: (meta.duration||1000)/100}, function(event, interval, index) { timer: can.core.Timer({interval: 100, length: (meta.duration||1000)/100}, function(event, interval, index) {
if (index > 30) { ui.duration.innerHTML = index/10+(index%10==0?".0":"")+"s..." } if (index > 30) { ui.duration.innerHTML = index/10+(index%10==0?".0":"")+"s..." }
}, function() { can.page.style(can, ui._target, "margin-right", "-400px") }, function() { can.page.style(can, ui._target, "margin-right", "-400px"), delete(can.__toast)
can.onmotion.delay(can, function() { can.page.Remove(can, ui._target) }, 1000) can.onmotion.delay(can, function() { can.page.Remove(can, ui._target) }, 1000)
delete(can.__toast)
}), _target: ui._target, }), _target: ui._target,
}); can.onmotion.story.auto(can, ui._target), meta.resize && can.onmotion.delayResize(can, ui._target, meta.resize) }); can.onmotion.story.auto(can, ui._target)
// , meta.resize && can.onmotion.delayResize(can, ui._target, meta.resize)
if (meta.action && meta.action.length == 1 && meta.action[0] === "") { if (meta.action && meta.action.length == 1 && meta.action[0] === "") {
can.page.Select(can, action._target, html.DIV_ACTION, function(target) { can.onmotion.hidden(can, target) }) can.page.Select(can, action._target, html.DIV_ACTION, function(target) { can.onmotion.hidden(can, target) })
} } can._toast && (can._toast.close(), delete(can._toast)), can._root.Action._toast.scrollTop += 10000
can._toast && (can._toast.close(), delete(can._toast))
if (meta.hash) { var list = can._root.Action._toastList = can._root.Action._toastList||{} if (meta.hash) { var list = can._root.Action._toastList = can._root.Action._toastList||{}
list[meta.hash] && can.page.insertBefore(can, action._target, list[meta.hash]._target) list[meta.hash] && can.page.insertBefore(can, action._target, list[meta.hash]._target), can.__toast = action
list[meta.hash] && (can.page.Remove(can, list[meta.hash]._target), list[meta.hash].close(), delete(list[meta.hash])), list[meta.hash] = action list[meta.hash] && (can.page.Remove(can, list[meta.hash]._target), list[meta.hash].close(), delete(list[meta.hash])), list[meta.hash] = action
can.__toast = action
} else { can._toast = action } return action } else { can._toast = action } return action
}, },
space: function(can) { return can.Conf(web.SPACE)||can.Conf(ice.POD)||can.misc.Search(can, ice.POD) }, space: function(can) { return can.Conf(web.SPACE)||can.Conf(ice.POD)||can.misc.Search(can, ice.POD) },
@ -217,6 +211,8 @@ Volcanos("user", {
if (item.value == "" && need[item.name] == "must") { err = true, item.focus(), can.user.toast(can, item.name+" 是必选字段, 请重新输入") } if (item.value == "" && need[item.name] == "must") { err = true, item.focus(), can.user.toast(can, item.name+" 是必选字段, 请重新输入") }
return item.name && args.push(item.name, item.value||""), data[item.name] = item.value||"" return item.name && args.push(item.name, item.value||""), data[item.name] = item.value||""
}); if (err) { return } can.onkeymap.prevent(event) }); if (err) { return } can.onkeymap.prevent(event)
var _msg = can.request(event); _msg.Option(ctx.ACTION, msg.Option(ctx.ACTION))
_msg.Option("_toast", msg.Option("_toast"))
can.core.CallFunc(cb, {event: can.request(event, {_handle: ice.TRUE})._event, button: button, data: data, list: list, args: args, input: action}) || action.cancel() can.core.CallFunc(cb, {event: can.request(event, {_handle: ice.TRUE})._event, button: button, data: data, list: list, args: args, input: action}) || action.cancel()
}, _target: ui._target, _engine: function(event, can, button) { action.submit(event, can, button) }, }, _target: ui._target, _engine: function(event, can, button) { action.submit(event, can, button) },
}); });

View File

@ -39,7 +39,10 @@ Volcanos(chat.ONACTION, {_init: function(can, target) { can.db.list = can.misc.S
}) })
can._toast = can.page.Append(can, can._target, ["toast"])._target can._toast = can.page.Append(can, can._target, ["toast"])._target
}, },
onsize: function(can, msg, height, width) { can.Conf({height: can.base.Min(height, 240), width: width}) }, onsize: function(can, msg, height, width) {
can.Conf({height: can.base.Min(height, 240), width: width})
can.page.style(can, can._toast, html.MAX_HEIGHT, can.page.height()-can.getHeaderHeight()-can.getFooterHeight()-(html.PLUGIN_MARGIN+html.PLUGIN_PADDING+html.ACTION_HEIGHT))
},
onlogin: function(can, msg) { onlogin: function(can, msg) {
can.Conf(html.MARGIN_Y, 2*html.PLUGIN_PADDING+2*html.PLUGIN_MARGIN+html.ACTION_HEIGHT) can.Conf(html.MARGIN_Y, 2*html.PLUGIN_PADDING+2*html.PLUGIN_MARGIN+html.ACTION_HEIGHT)
can.Conf(html.MARGIN_X, 2*html.PLUGIN_PADDING+2*html.PLUGIN_MARGIN) can.Conf(html.MARGIN_X, 2*html.PLUGIN_PADDING+2*html.PLUGIN_MARGIN)

View File

@ -58,7 +58,7 @@ var Volcanos = shy({iceberg: "", volcano: "", frame: chat.FRAME_JS, _cache: {},
can.core.List(arguments, function(item, index) { if (!item || index == 0) { return } can.core.Item(item, set) }); return msg can.core.List(arguments, function(item, index) { if (!item || index == 0) { return } can.core.Item(item, set) }); return msg
}, },
requestPodCmd: function(event) { return can.request(event, {pod: can.ConfSpace(), index: can.ConfIndex()}) }, requestPodCmd: function(event) { return can.request(event, {pod: can.ConfSpace(), index: can.ConfIndex()}) },
requestAction: function(event, button) { return can.request(event, {action: button, _toast: event.isTrusted? ice.PROCESS+" "+button: ""}) }, requestAction: function(event, button) { return can.request(event, {action: button, _toast: event.isTrusted? button+lex.SP+ice.PROCESS: ""}) },
runActionInputs: function(event, cmds, cb) { var msg = can.request(event), meta = can.Conf() runActionInputs: function(event, cmds, cb) { var msg = can.request(event), meta = can.Conf()
if (msg.Option(ice.MSG_HANDLE) != ice.TRUE && cmds && cmds[0] == ctx.ACTION && meta.feature[cmds[1]]) { var msg = can.request(event, {action: cmds[1]}) if (msg.Option(ice.MSG_HANDLE) != ice.TRUE && cmds && cmds[0] == ctx.ACTION && meta.feature[cmds[1]]) { var msg = can.request(event, {action: cmds[1]})
if (can.base.isFunc(meta.feature[cmds[1]])) { return meta.feature[cmds[1]](can, msg, cmds.slice(2)) } if (can.base.isFunc(meta.feature[cmds[1]])) { return meta.feature[cmds[1]](can, msg, cmds.slice(2)) }