Further improve the remote font intergation: Better preload strategy, better loading and UI interation.
This commit is contained in:
@@ -29,11 +29,13 @@ export class Result {
|
||||
* @param {string} name Result type
|
||||
* @param {Info} info Result info
|
||||
* @param {object} control Result controller
|
||||
* @param {string} ui User interfact this command will use
|
||||
*/
|
||||
constructor(name, info, control) {
|
||||
constructor(name, info, control, ui) {
|
||||
this.name = name;
|
||||
this.info = info;
|
||||
this.control = control;
|
||||
this.ui = ui;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +503,16 @@ class Wizard {
|
||||
return this.built.started();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the control info of current wizard
|
||||
*
|
||||
* @returns {object}
|
||||
*
|
||||
*/
|
||||
control() {
|
||||
return this.built.control();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close current wizard
|
||||
*
|
||||
|
||||
5
ui/commands/controls.js
vendored
5
ui/commands/controls.js
vendored
@@ -44,18 +44,17 @@ export class Controls {
|
||||
* Get a control
|
||||
*
|
||||
* @param {string} type Type of the control
|
||||
* @param {...any} data Data needed to build the control
|
||||
*
|
||||
* @returns {object} Control object
|
||||
*
|
||||
* @throws {Exception} When given control type is undefined
|
||||
*
|
||||
*/
|
||||
get(type, ...data) {
|
||||
get(type) {
|
||||
if (typeof this.controls[type] !== "object") {
|
||||
throw new Exception('Control "' + type + '" was undefined');
|
||||
}
|
||||
|
||||
return this.controls[type].build(...data);
|
||||
return this.controls[type];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ class Wizard {
|
||||
credential: ""
|
||||
};
|
||||
this.step = subs;
|
||||
this.controls = controls;
|
||||
this.controls = controls.get("SSH");
|
||||
this.history = history;
|
||||
|
||||
this.step.resolve(this.stepInitialPrompt());
|
||||
@@ -497,6 +497,10 @@ class Wizard {
|
||||
return this.hasStarted;
|
||||
}
|
||||
|
||||
control() {
|
||||
return this.controls;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.step.resolve(
|
||||
this.stepErrorDone(
|
||||
@@ -603,7 +607,7 @@ class Wizard {
|
||||
new command.Result(
|
||||
configInput.user + "@" + configInput.host,
|
||||
self.info,
|
||||
self.controls.get("SSH", {
|
||||
self.controls.build({
|
||||
charset: configInput.charset,
|
||||
send(data) {
|
||||
return commandHandler.sendData(data);
|
||||
@@ -615,7 +619,8 @@ class Wizard {
|
||||
return commandHandler.sendResize(rows, cols);
|
||||
},
|
||||
events: commandHandler.events
|
||||
})
|
||||
}),
|
||||
self.controls.ui()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -249,7 +249,7 @@ class Wizard {
|
||||
this.config = config;
|
||||
this.session = session;
|
||||
this.step = subs;
|
||||
this.controls = controls;
|
||||
this.controls = controls.get("Telnet");
|
||||
this.history = history;
|
||||
|
||||
this.step.resolve(this.stepInitialPrompt());
|
||||
@@ -259,6 +259,10 @@ class Wizard {
|
||||
return this.hasStarted;
|
||||
}
|
||||
|
||||
control() {
|
||||
return this.controls;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.step.resolve(
|
||||
this.stepErrorDone(
|
||||
@@ -337,7 +341,7 @@ class Wizard {
|
||||
new command.Result(
|
||||
configInput.host,
|
||||
self.info,
|
||||
self.controls.get("Telnet", {
|
||||
self.controls.build({
|
||||
charset: parsedConfig.charset,
|
||||
send(data) {
|
||||
return commandHandler.sendData(data);
|
||||
@@ -346,7 +350,8 @@ class Wizard {
|
||||
return commandHandler.sendClose();
|
||||
},
|
||||
events: commandHandler.events
|
||||
})
|
||||
}),
|
||||
self.controls.ui()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -100,10 +100,6 @@ class Control {
|
||||
this.resizer(dim.rows, dim.cols);
|
||||
}
|
||||
|
||||
ui() {
|
||||
return "Console";
|
||||
}
|
||||
|
||||
enabled() {
|
||||
this.enable = true;
|
||||
}
|
||||
@@ -168,6 +164,10 @@ export class SSH {
|
||||
return "SSH";
|
||||
}
|
||||
|
||||
ui() {
|
||||
return "Console";
|
||||
}
|
||||
|
||||
build(data) {
|
||||
return new Control(data, this.color);
|
||||
}
|
||||
|
||||
@@ -428,10 +428,6 @@ class Control {
|
||||
this.parser.requestWindowResize();
|
||||
}
|
||||
|
||||
ui() {
|
||||
return "Console";
|
||||
}
|
||||
|
||||
enabled() {
|
||||
this.enable = true;
|
||||
}
|
||||
@@ -527,6 +523,10 @@ export class Telnet {
|
||||
return "Telnet";
|
||||
}
|
||||
|
||||
ui() {
|
||||
return "Console";
|
||||
}
|
||||
|
||||
build(data) {
|
||||
return new Control(data, this.color);
|
||||
}
|
||||
|
||||
10
ui/home.css
10
ui/home.css
@@ -369,16 +369,6 @@
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
font-size: 1.2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#home-content-preload-drop {
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
37
ui/home.vue
37
ui/home.vue
@@ -104,8 +104,6 @@
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="home-content-preload-drop"></div>
|
||||
</screens>
|
||||
|
||||
<connect-widget
|
||||
@@ -470,6 +468,7 @@ export default {
|
||||
name: data.name,
|
||||
info: data.info,
|
||||
control: data.control,
|
||||
ui: data.ui,
|
||||
toolbar: false,
|
||||
indicator: {
|
||||
level: "",
|
||||
@@ -534,22 +533,40 @@ export default {
|
||||
}
|
||||
},
|
||||
tabWarning(index, msg) {
|
||||
if (msg.length > 0) {
|
||||
this.tab.tabs[index].indicator.message = msg;
|
||||
this.tab.tabs[index].indicator.level = "warning";
|
||||
} else {
|
||||
if (msg.toDismiss) {
|
||||
if (
|
||||
this.tab.tabs[index].indicator.message !== msg.text ||
|
||||
this.tab.tabs[index].indicator.level !== "warning"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tab.tabs[index].indicator.message = "";
|
||||
this.tab.tabs[index].indicator.level = "";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.tab.tabs[index].indicator.message = msg.text;
|
||||
this.tab.tabs[index].indicator.level = "warning";
|
||||
},
|
||||
tabInfo(index, msg) {
|
||||
if (msg.length > 0) {
|
||||
this.tab.tabs[index].indicator.message = msg;
|
||||
this.tab.tabs[index].indicator.level = "info";
|
||||
} else {
|
||||
if (msg.toDismiss) {
|
||||
if (
|
||||
this.tab.tabs[index].indicator.message !== msg.text ||
|
||||
this.tab.tabs[index].indicator.level !== "info"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tab.tabs[index].indicator.message = "";
|
||||
this.tab.tabs[index].indicator.level = "";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.tab.tabs[index].indicator.message = msg.text;
|
||||
this.tab.tabs[index].indicator.level = "info";
|
||||
},
|
||||
tabUpdated(index) {
|
||||
this.$emit("tab-updated", this.tab.tabs);
|
||||
|
||||
@@ -212,6 +212,15 @@
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div
|
||||
v-if="preloaderIDName.length > 0"
|
||||
style="width: 1px; height: 1px; margin: 10px; position: absolute; top: 0; bottom: 0; overflow: hidden;"
|
||||
>
|
||||
<div :id="preloaderIDName">
|
||||
{{ current.title || connector.name }} wizard
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
@@ -219,6 +228,8 @@
|
||||
import "./connector.css";
|
||||
import * as command from "../commands/commands.js";
|
||||
|
||||
const preloaderIDPrefix = "connector-resource-preload-control-";
|
||||
|
||||
function buildField(i, field) {
|
||||
return {
|
||||
verified: false,
|
||||
@@ -267,6 +278,7 @@ export default {
|
||||
currentConnector: null,
|
||||
currentConnectorCloseWait: null,
|
||||
current: buildEmptyCurrent(),
|
||||
preloaderIDName: "",
|
||||
working: false,
|
||||
disabled: false,
|
||||
cancelled: false
|
||||
@@ -399,6 +411,13 @@ export default {
|
||||
throw new Error("Cannot run wizard multiple times");
|
||||
}
|
||||
|
||||
this.preloaderIDName =
|
||||
preloaderIDPrefix +
|
||||
this.getConnector()
|
||||
.wizard.control()
|
||||
.ui()
|
||||
.toLowerCase();
|
||||
|
||||
this.currentConnectorCloseWait = (async () => {
|
||||
while (!this.disabled) {
|
||||
let next = this.buildCurrent(await this.getConnector().wizard.next());
|
||||
|
||||
@@ -21,14 +21,18 @@
|
||||
|
||||
@import "~hack-font/build/web/hack.css";
|
||||
|
||||
#home-content > #home-content-preload-drop::before {
|
||||
#connector-resource-preload-control-console {
|
||||
font-family: Hack;
|
||||
}
|
||||
#connector-resource-preload-control-console::after {
|
||||
content: " ";
|
||||
font-family: Hack, monospace;
|
||||
font-family: Hack;
|
||||
font-weight: bold;
|
||||
}
|
||||
#home-content > #home-content-preload-drop::after {
|
||||
#connector-resource-preload-control-console::before {
|
||||
content: " ";
|
||||
font-family: Hack, monospace;
|
||||
font-family: Hack;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#home-content > .screen > .screen-screen > .screen-console {
|
||||
@@ -144,5 +148,32 @@
|
||||
}
|
||||
|
||||
#home-content > .screen > .screen-screen > .screen-console > .console-console {
|
||||
font-family: Hack, monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#home-content
|
||||
> .screen
|
||||
> .screen-screen
|
||||
> .screen-console
|
||||
> .console-console
|
||||
> .console-loading {
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
#home-content
|
||||
> .screen
|
||||
> .screen-screen
|
||||
> .screen-console
|
||||
> .console-console
|
||||
> .console-loading
|
||||
> .console-loading-icon {
|
||||
background: url(./busy.svg) 50% no-repeat;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,16 @@
|
||||
<div
|
||||
class="console-console"
|
||||
style="top: 0; right: 0; left: 0; bottom: 0; padding: 0; margin: 0; z-index: 0; position: absolute; overflow: hidden"
|
||||
:style="'font-family: ' + typeface"
|
||||
>
|
||||
<h2 style="display:none;">Console</h2>
|
||||
|
||||
<div class="console-loading">
|
||||
<div class="console-loading-icon"></div>
|
||||
<div class="console-loading-message">
|
||||
Initializing console ...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
@@ -91,7 +99,13 @@ import "xterm/css/xterm.css";
|
||||
|
||||
const termTypeFace = "Hack";
|
||||
const termFallbackTypeFace = "monospace";
|
||||
const termTypeFaceLoadTimeout = 10000;
|
||||
const termTypeFaceLoadTimeout = 3000;
|
||||
const termTypeFaceLoadError =
|
||||
'Remote font "' +
|
||||
termTypeFace +
|
||||
'" is unavailable, using "' +
|
||||
termFallbackTypeFace +
|
||||
'" instead until the remote font is loaded';
|
||||
const termDefaultFontSize = 16;
|
||||
const termMinFontSize = 14;
|
||||
const termMaxFontSize = 36;
|
||||
@@ -205,7 +219,11 @@ class Term {
|
||||
});
|
||||
}
|
||||
|
||||
open(root, callbacks) {
|
||||
setFont(value) {
|
||||
this.term.setOption("fontFamily", value);
|
||||
}
|
||||
|
||||
init(root, callbacks) {
|
||||
this.term.open(root);
|
||||
|
||||
this.term.textarea.addEventListener("focus", callbacks.focus);
|
||||
@@ -248,32 +266,6 @@ class Term {
|
||||
this.refit();
|
||||
}
|
||||
|
||||
init(root, callbacks) {
|
||||
const self = this;
|
||||
|
||||
return Promise.all([
|
||||
new FontFaceObserver(termTypeFace).load(null, termTypeFaceLoadTimeout),
|
||||
new FontFaceObserver(termTypeFace, { weight: "bold" }).load(
|
||||
null,
|
||||
termTypeFaceLoadTimeout
|
||||
)
|
||||
])
|
||||
.then(() => {
|
||||
self.open(root, callbacks);
|
||||
})
|
||||
.catch(() => {
|
||||
callbacks.warn(
|
||||
"Unable to load remote font, using " +
|
||||
termFallbackTypeFace +
|
||||
" instead"
|
||||
);
|
||||
|
||||
self.term.setOption("fontFamily", termFallbackTypeFace);
|
||||
|
||||
self.open(root, callbacks);
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(event) {
|
||||
try {
|
||||
this.term.textarea.dispatchEvent(event);
|
||||
@@ -388,6 +380,8 @@ export default {
|
||||
return {
|
||||
screenKeys: consoleScreenKeys,
|
||||
term: new Term(this.control),
|
||||
typeface: termTypeFace,
|
||||
running: true,
|
||||
runner: null
|
||||
};
|
||||
},
|
||||
@@ -407,19 +401,87 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.running = true;
|
||||
|
||||
await this.init();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.deinit();
|
||||
this.running = false;
|
||||
},
|
||||
methods: {
|
||||
loadRemoteFont(typeface, timeout) {
|
||||
return Promise.all([
|
||||
new FontFaceObserver(typeface).load(null, timeout),
|
||||
new FontFaceObserver(typeface, {
|
||||
weight: "bold"
|
||||
}).load(null, timeout)
|
||||
]);
|
||||
},
|
||||
retryLoadRemoteFont(typeface, timeout, onSuccess) {
|
||||
const self = this;
|
||||
|
||||
self
|
||||
.loadRemoteFont(typeface, timeout)
|
||||
.then(() => {
|
||||
onSuccess();
|
||||
})
|
||||
.catch(() => {
|
||||
if (!self.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.retryLoadRemoteFont(typeface, timeout, onSuccess);
|
||||
});
|
||||
},
|
||||
async openTerm(root, callbacks) {
|
||||
const self = this;
|
||||
|
||||
return self
|
||||
.loadRemoteFont(termTypeFace, termTypeFaceLoadTimeout)
|
||||
.then(() => {
|
||||
if (!self.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.innerHTML = "";
|
||||
|
||||
self.term.init(root, callbacks);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!self.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.innerHTML = "";
|
||||
|
||||
callbacks.warn(termTypeFaceLoadError, false);
|
||||
|
||||
self.term.setFont(termFallbackTypeFace);
|
||||
self.term.init(root, callbacks);
|
||||
|
||||
self.retryLoadRemoteFont(
|
||||
termTypeFace,
|
||||
termTypeFaceLoadTimeout,
|
||||
() => {
|
||||
if (!self.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.term.setFont(termTypeFace);
|
||||
|
||||
callbacks.warn(termTypeFaceLoadError, true);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
triggerActive() {
|
||||
this.active ? this.activate() : this.deactivate();
|
||||
},
|
||||
async init() {
|
||||
let self = this;
|
||||
|
||||
await this.term.init(
|
||||
await this.openTerm(
|
||||
this.$el.getElementsByClassName("console-console")[0],
|
||||
{
|
||||
focus(e) {
|
||||
@@ -430,15 +492,20 @@ export default {
|
||||
document.removeEventListener("keyup", self.localKeypress);
|
||||
document.removeEventListener("keydown", self.localKeypress);
|
||||
},
|
||||
warn(msg) {
|
||||
self.$emit("warning", msg);
|
||||
warn(msg, toDismiss) {
|
||||
self.$emit("warning", {
|
||||
text: msg,
|
||||
toDismiss: toDismiss
|
||||
});
|
||||
},
|
||||
info(msg) {
|
||||
self.$emit("info", msg);
|
||||
info(msg, toDismiss) {
|
||||
self.$emit("info", {
|
||||
text: msg,
|
||||
toDismiss: toDismiss
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.triggerActive();
|
||||
this.runRunner();
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<div class="screen-screen" style="position: relative">
|
||||
<component
|
||||
:is="getComponent(screenInfo.control.ui())"
|
||||
:is="getComponent(screenInfo.ui)"
|
||||
:active="screen === idx"
|
||||
:control="screenInfo.control"
|
||||
:change="screenInfo.indicator"
|
||||
|
||||
Reference in New Issue
Block a user