mirror of
https://github.com/HeyPuter/puter
synced 2024-11-15 06:15:47 +00:00
Merge pull request #442 from HeyPuter/eric/extensibility
class registry for service scripts
This commit is contained in:
commit
0672562c8a
@ -58,6 +58,10 @@ export default [
|
||||
"html_encode": true,
|
||||
"html_decode": true,
|
||||
"isMobile": true,
|
||||
// Class Registry
|
||||
"logger": true,
|
||||
"def": true,
|
||||
"use": true,
|
||||
// Libraries
|
||||
"saveAs": true, // FileSaver
|
||||
"iro": true, // iro.js color picker
|
||||
|
@ -114,6 +114,12 @@ class PuterHomepageService extends BaseService {
|
||||
|
||||
const bundled = env != 'dev' || use_bundled_gui;
|
||||
|
||||
const writeScriptTag = path =>
|
||||
`<script type="${
|
||||
Array.isArray(path) ? 'text/javascirpt' : 'module'
|
||||
}" src="${Array.isArray(path) ? path[0] : path}"></script>\n`
|
||||
;
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
@ -161,6 +167,28 @@ class PuterHomepageService extends BaseService {
|
||||
<!-- Preload images when applicable -->
|
||||
<link rel="preload" as="image" href="${asset_dir}/images/wallpaper.webp">
|
||||
|
||||
<script>
|
||||
if ( ! window.service_script ) {
|
||||
window.service_script_api_promise = (() => {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
promise.resolve = resolve;
|
||||
promise.reject = reject;
|
||||
return promise;
|
||||
})();
|
||||
window.service_script = async fn => {
|
||||
try {
|
||||
await fn(await window.service_script_api_promise);
|
||||
} catch (e) {
|
||||
console.error('service_script(ERROR)', e);
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Files from JSON (may be empty) -->
|
||||
${
|
||||
((!bundled && manifest?.css_paths)
|
||||
@ -209,13 +237,16 @@ class PuterHomepageService extends BaseService {
|
||||
|
||||
${
|
||||
((!bundled && manifest?.js_paths)
|
||||
? manifest.js_paths.map(path => `<script type="module" src="${path}"></script>\n`)
|
||||
? manifest.js_paths.map(path => writeScriptTag(path))
|
||||
: []).join('')
|
||||
}
|
||||
<!-- Load the GUI script -->
|
||||
<script ${ !bundled ? ' type="module"' : ''} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
|
||||
<script ${
|
||||
// !bundled ? ' type="module"' : ''
|
||||
' type="module"'
|
||||
} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
|
||||
<!-- Initialize GUI when document is loaded -->
|
||||
<script>
|
||||
<script type="module">
|
||||
window.addEventListener('load', function() {
|
||||
gui(${
|
||||
// TODO: override JSON.stringify to ALWAYS to this...
|
||||
|
@ -24,7 +24,8 @@
|
||||
"/css/theme.css"
|
||||
],
|
||||
"js_paths": [
|
||||
"/src/initgui.js",
|
||||
"/src/init_sync.js",
|
||||
"/src/init_async.js",
|
||||
"/src/helpers.js",
|
||||
"/src/IPC.js",
|
||||
"/src/globals.js",
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class Button extends Component {
|
||||
static ID = 'ui.component.Button';
|
||||
|
||||
export default class Button extends Component {
|
||||
static PROPERTIES = {
|
||||
label: { value: 'Test Label' },
|
||||
on_click: { value: null },
|
||||
@ -63,6 +65,4 @@ export default class Button extends Component {
|
||||
$(this.dom_).find('button').prop('disabled', ! enabled);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-button', Button);
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class CodeEntryView extends Component {
|
||||
static ID = 'ui.component.CodeEntryView';
|
||||
|
||||
export default class CodeEntryView extends Component {
|
||||
static PROPERTIES = {
|
||||
value: {},
|
||||
error: {},
|
||||
@ -213,6 +215,4 @@ export default class CodeEntryView extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-code-entry-view', CodeEntryView);
|
||||
})
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* Display a list of checkboxes for the user to confirm.
|
||||
*/
|
||||
export default class ConfirmationsView extends Component {
|
||||
export default def(class ConfirmationsView extends Component {
|
||||
static ID = 'ui.component.ConfirmationsView';
|
||||
|
||||
static PROPERTIES = {
|
||||
confirmations: {
|
||||
description: 'The list of confirmations to display',
|
||||
@ -56,6 +58,4 @@ export default class ConfirmationsView extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-confirmations-view', ConfirmationsView);
|
||||
});
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* Allows a flex layout of composed components to be
|
||||
* treated as a component.
|
||||
*/
|
||||
export default class Flexer extends Component {
|
||||
export default def(class Flexer extends Component {
|
||||
static ID = 'ui.component.Flexer';
|
||||
|
||||
static PROPERTIES = {
|
||||
children: {},
|
||||
gap: { value: '20pt' },
|
||||
@ -36,6 +38,4 @@ export default class Flexer extends Component {
|
||||
$(this.dom_).find('div').first().css('gap', gap);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-flexer', Flexer);
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* Allows using an HTML string as a component.
|
||||
*/
|
||||
export default class JustHTML extends Component {
|
||||
export default def(class JustHTML extends Component {
|
||||
static ID = 'ui.component.JustHTML';
|
||||
|
||||
static PROPERTIES = { html: { value: '' } };
|
||||
create_template ({ template }) {
|
||||
$(template).html(`<span></span>`);
|
||||
@ -13,6 +15,4 @@ export default class JustHTML extends Component {
|
||||
$(this.dom_).find('span').html(html);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-just-html', JustHTML);
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class PasswordEntry extends Component {
|
||||
static ID = 'ui.component.PasswordEntry';
|
||||
|
||||
export default class PasswordEntry extends Component {
|
||||
static PROPERTIES = {
|
||||
spec: {},
|
||||
value: {},
|
||||
@ -131,6 +133,4 @@ export default class PasswordEntry extends Component {
|
||||
$(this.dom_).find("#toggle-show-password").attr("src", show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-password-entry', PasswordEntry);
|
||||
});
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
import UIComponentWindow from "../UIComponentWindow.js";
|
||||
|
||||
export default class QRCodeView extends Component {
|
||||
export default def(class QRCodeView extends Component {
|
||||
static ID = 'ui.component.QRCodeView';
|
||||
|
||||
static PROPERTIES = {
|
||||
value: {
|
||||
description: 'The text to encode in the QR code',
|
||||
@ -76,6 +78,4 @@ export default class QRCodeView extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-qr-code', QRCodeView);
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default class RecoveryCodeEntryView extends Component {
|
||||
export default def(class RecoveryCodeEntryView extends Component {
|
||||
static ID = 'ui.component.RecoveryCodeEntryView';
|
||||
static PROPERTIES = {
|
||||
value: {},
|
||||
length: { value: 8 },
|
||||
@ -82,6 +83,4 @@ export default class RecoveryCodeEntryView extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-recovery-code-entry', RecoveryCodeEntryView);
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class RecoveryCodesView extends Component {
|
||||
static ID = 'ui.component.RecoveryCodesView';
|
||||
|
||||
export default class RecoveryCodesView extends Component {
|
||||
static PROPERTIES = {
|
||||
values: {
|
||||
description: 'The recovery codes to display',
|
||||
@ -89,6 +91,4 @@ export default class RecoveryCodesView extends Component {
|
||||
print_frame.contentWindow.window.print();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-recovery-codes-view', RecoveryCodesView);
|
||||
});
|
||||
|
@ -16,12 +16,14 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* Slider: A labeled slider input.
|
||||
*/
|
||||
export default class Slider extends Component {
|
||||
export default def(class Slider extends Component {
|
||||
static ID = 'ui.component.Slider';
|
||||
|
||||
static PROPERTIES = {
|
||||
name: { value: null },
|
||||
label: { value: null },
|
||||
@ -109,6 +111,4 @@ export default class Slider extends Component {
|
||||
input.value = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-slider', Slider);
|
||||
});
|
||||
|
41
src/UI/Components/Spinner.js
Normal file
41
src/UI/Components/Spinner.js
Normal file
@ -0,0 +1,41 @@
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class Spinner extends Component {
|
||||
static ID = 'ui.component.Spinner';
|
||||
|
||||
static PROPERTIES = {}
|
||||
// static RENDER_MODE = Component.NO_SHADOW;
|
||||
|
||||
create_template ({ template }) {
|
||||
console.log('template?', template);
|
||||
|
||||
template.innerHTML = /*html*/`
|
||||
<div>
|
||||
<svg style="display:block; margin: 0 auto; " xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<title>circle anim</title>
|
||||
<g fill="#212121" class="nc-icon-wrapper">
|
||||
<g class="nc-loop-circle-24-icon-f">
|
||||
<path d="M12 24a12 12 0 1 1 12-12 12.013 12.013 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10.011 10.011 0 0 0 12 2z" fill="#212121" opacity=".4"></path>
|
||||
<path d="M24 12h-2A10.011 10.011 0 0 0 12 2V0a12.013 12.013 0 0 1 12 12z" data-color="color-2"></path>
|
||||
</g>
|
||||
<style>
|
||||
.nc-loop-circle-24-icon-f{
|
||||
--animation-duration:0.5s;
|
||||
transform-origin:12px 12px;
|
||||
animation:nc-loop-circle-anim var(--animation-duration) infinite linear
|
||||
}
|
||||
@keyframes nc-loop-circle-anim{
|
||||
0%{
|
||||
transform:rotate(0)
|
||||
}
|
||||
100%{
|
||||
transform:rotate(360deg)
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
@ -1,11 +1,13 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* StepHeading renders a heading with a leading symbol.
|
||||
* The leading symbol is styled inside a cricle and is
|
||||
* optimized for single-digit numbers.
|
||||
*/
|
||||
export default class StepHeading extends Component {
|
||||
export default def(class StepHeading extends Component {
|
||||
static ID = 'ui.component.StepHeading';
|
||||
|
||||
static PROPERTIES = {
|
||||
symbol: {
|
||||
description: 'The symbol to display',
|
||||
@ -56,6 +58,4 @@ export default class StepHeading extends Component {
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-step-heading', StepHeading);
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
export default def(class StepView extends Component {
|
||||
static ID = 'ui.component.StepView';
|
||||
|
||||
export default class StepView extends Component {
|
||||
static PROPERTIES = {
|
||||
children: {},
|
||||
done: { value: false },
|
||||
@ -62,6 +64,4 @@ export default class StepView extends Component {
|
||||
}
|
||||
this.set('position', this.get('position') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-step-view', StepView);
|
||||
});
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* A simple component that displays a string in the
|
||||
* specified style.
|
||||
*/
|
||||
export default class StringView extends Component {
|
||||
export default def(class StringView extends Component {
|
||||
static ID = 'ui.component.StringView';
|
||||
|
||||
static PROPERTIES = {
|
||||
text: { value: '' },
|
||||
heading: { value: 0 },
|
||||
@ -40,6 +42,4 @@ export default class StringView extends Component {
|
||||
either({ heading: this.get('heading'), text });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-string-view', StringView);
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* A table with a sticky header
|
||||
*/
|
||||
export default class Table extends Component {
|
||||
export default def(class Table extends Component {
|
||||
static ID = 'ui.component.Table';
|
||||
|
||||
static PROPERTIES = {
|
||||
headings: { value: [] },
|
||||
scale: { value: '2pt' },
|
||||
@ -78,6 +80,4 @@ export default class Table extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-table', Table);
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Component, defineComponent } from "../../util/Component.js";
|
||||
const Component = use('util.Component');
|
||||
|
||||
/**
|
||||
* A simple component when you just need to test something.
|
||||
*/
|
||||
export default class TestView extends Component {
|
||||
export default def(class TestView extends Component {
|
||||
static ID = 'ui.component.TestView';
|
||||
|
||||
static CSS = `
|
||||
div {
|
||||
background-color: lightblue;
|
||||
@ -17,6 +19,4 @@ export default class TestView extends Component {
|
||||
<div>I am a test view</div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
defineComponent('c-test-view', TestView);
|
||||
});
|
||||
|
@ -49,6 +49,7 @@ const end_process = async (uuid, force) => {
|
||||
};
|
||||
|
||||
class TaskManagerTable extends Component {
|
||||
static ID = 'ui.component.TaskManagerTable';
|
||||
static PROPERTIES = {
|
||||
tasks: { value: [] },
|
||||
};
|
||||
@ -157,9 +158,11 @@ class TaskManagerTable extends Component {
|
||||
return rows;
|
||||
};
|
||||
}
|
||||
defineComponent('c-task-manager-table', TaskManagerTable);
|
||||
defineComponent(TaskManagerTable);
|
||||
|
||||
class TaskManagerRow extends Component {
|
||||
static ID = 'ui.component.TaskManagerRow';
|
||||
|
||||
static PROPERTIES = {
|
||||
name: {},
|
||||
uuid: {},
|
||||
@ -291,7 +294,7 @@ class TaskManagerRow extends Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
defineComponent('c-task-manager-row', TaskManagerRow);
|
||||
defineComponent(TaskManagerRow);
|
||||
|
||||
const UIWindowTaskManager = async function UIWindowTaskManager () {
|
||||
const svc_process = globalThis.services.get('process');
|
||||
|
@ -17,6 +17,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
export class Service {
|
||||
construct () {
|
||||
if ( ! this._construct ) return;
|
||||
return this._construct();
|
||||
}
|
||||
init (...a) {
|
||||
if ( ! this._init ) return;
|
||||
return this._init(...a)
|
||||
|
18
src/index.js
18
src/index.js
@ -16,25 +16,9 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
window.service_script_api_promise = (() => {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
promise.resolve = resolve;
|
||||
promise.reject = reject;
|
||||
return promise;
|
||||
})();
|
||||
window.service_script = async fn => {
|
||||
try {
|
||||
await fn(await window.service_script_api_promise);
|
||||
} catch (e) {
|
||||
console.error('service_script(ERROR)', e);
|
||||
}
|
||||
};
|
||||
|
||||
window.puter_gui_enabled = true;
|
||||
|
||||
/**
|
||||
* Initializes and configures the GUI (Graphical User Interface) settings based on the provided options.
|
||||
*
|
||||
|
8
src/init_async.js
Normal file
8
src/init_async.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Note: this logs AFTER all imports because imports are hoisted
|
||||
logger.info('start -> async initialization');
|
||||
|
||||
import './util/TeePromise.js';
|
||||
import './util/Component.js';
|
||||
|
||||
logger.info('end -> async initialization');
|
||||
globalThis.init_promise.resolve();
|
137
src/init_sync.js
Normal file
137
src/init_sync.js
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @global
|
||||
* @function logger
|
||||
* @param {Array<any>} a - The arguments.
|
||||
*/
|
||||
/**
|
||||
* @global
|
||||
* @function use
|
||||
* @param {string} arg - The string argument.
|
||||
* @returns {any} The return value.
|
||||
*/
|
||||
/**
|
||||
* @global
|
||||
* @function def
|
||||
* @param {any} arg - The argument.
|
||||
* @returns {any} The return value.
|
||||
*/
|
||||
|
||||
// An initial logger to log do before we get a more fancy logger
|
||||
// (which we never really do yet, at the time of writing this);
|
||||
// something like this was also done in backend and it proved useful.
|
||||
(scope => {
|
||||
globalThis.logger = {
|
||||
info: (...a) => console.log('%c[INIT/INFO]', 'color: #4287f5', ...a),
|
||||
};
|
||||
})(globalThis);
|
||||
logger.info('start -> blocking initialization');
|
||||
|
||||
// A global promise (like TeePromise, except we can't import anything yet)
|
||||
// that will be resolved by `init_async.js` when it completes.
|
||||
(scope => {
|
||||
scope.init_promise = (() => {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
promise.resolve = resolve;
|
||||
promise.reject = reject;
|
||||
return promise;
|
||||
})();
|
||||
})(globalThis);
|
||||
|
||||
// This is where `use()` and `def()` are defined.
|
||||
//
|
||||
// A global registry for class definitions. This allows us to expose
|
||||
// classes to service scripts even when the frontend code is bundled.
|
||||
// Additionally, it allows us to create hooks upon class registration,
|
||||
// which we use to turn classes which extend HTMLElement into components
|
||||
// (i.e. give them tag names because that is required).
|
||||
//
|
||||
// It's worth noting `use()` and `def()` for service scripts is exposed
|
||||
// in initgui.js, in the `launch_services()` function. (at the time this
|
||||
// comment was written)
|
||||
(scope => {
|
||||
const registry_ = {
|
||||
classes_m: {},
|
||||
classes_l: [],
|
||||
hooks_on_register: [],
|
||||
};
|
||||
|
||||
const on_self_registered_api = {
|
||||
on_other_registered: hook => registry_.hooks_on_register.push(hook),
|
||||
}
|
||||
|
||||
scope.lib = {
|
||||
is_subclass (subclass, superclass) {
|
||||
if (subclass === superclass) return true;
|
||||
|
||||
let proto = subclass.prototype;
|
||||
while (proto) {
|
||||
if (proto === superclass.prototype) return true;
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
scope.def = (cls, id) => {
|
||||
id = id || cls.ID;
|
||||
if ( id === undefined ) {
|
||||
throw new Error('Class must have an ID');
|
||||
}
|
||||
|
||||
if ( registry_.classes_m[id] ) {
|
||||
throw new Error(`Class with ID ${id} already registered`);
|
||||
}
|
||||
|
||||
registry_.classes_m[id] = cls;
|
||||
registry_.classes_l.push(cls);
|
||||
|
||||
registry_.hooks_on_register.forEach(hook => hook({ cls }));
|
||||
|
||||
console.log('registered class', id, registry_);
|
||||
|
||||
// Find class that owns 'on_self_registered' hook
|
||||
let owner = cls;
|
||||
while (
|
||||
owner.__proto__ && owner.__proto__.on_self_registered
|
||||
&& owner.__proto__.on_self_registered === cls.on_self_registered
|
||||
) {
|
||||
owner = owner.__proto__;
|
||||
}
|
||||
|
||||
if ( cls.on_self_registered ) {
|
||||
cls.on_self_registered.call(cls, {
|
||||
...on_self_registered_api,
|
||||
is_owner: cls === owner,
|
||||
});
|
||||
}
|
||||
|
||||
return cls;
|
||||
};
|
||||
|
||||
scope.use = id => {
|
||||
console.log('use called with id: ', id);
|
||||
if ( id === undefined ) {
|
||||
return registry_.classes_m;
|
||||
}
|
||||
|
||||
if ( !registry_.classes_m[id] ) {
|
||||
throw new Error(`Class with ID ${id} not registered`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
'okay it\'s going to return:',
|
||||
registry_.classes_m[id],
|
||||
'and the entire map is this: ',
|
||||
registry_.classes_m
|
||||
)
|
||||
|
||||
return registry_.classes_m[id];
|
||||
}
|
||||
})(globalThis);
|
||||
|
||||
logger.info('end -> blocking initialization');
|
@ -40,7 +40,10 @@ import { ProcessService } from './services/ProcessService.js';
|
||||
import { PROCESS_RUNNING } from './definitions.js';
|
||||
import { LocaleService } from './services/LocaleService.js';
|
||||
import { SettingsService } from './services/SettingsService.js';
|
||||
|
||||
import UIComponentWindow from './UI/UIComponentWindow.js';
|
||||
import Spinner from './UI/Components/Spinner.js';
|
||||
|
||||
|
||||
const launch_services = async function () {
|
||||
// === Services Data Structures ===
|
||||
@ -54,6 +57,8 @@ const launch_services = async function () {
|
||||
services_m_[name] = instance;
|
||||
}
|
||||
|
||||
globalThis.def(UIComponentWindow, 'ui.UIComponentWindow');
|
||||
|
||||
// === Hooks for Service Scripts from Backend ===
|
||||
const service_script_deferred = { services: [], on_ready: [] };
|
||||
const service_script_api = {
|
||||
@ -61,7 +66,9 @@ const launch_services = async function () {
|
||||
on_ready: fn => service_script_deferred.on_ready.push(fn),
|
||||
// Some files can't be imported by service scripts,
|
||||
// so this hack makes that possible.
|
||||
use: name => ({ UIWindow, UIComponentWindow })[name],
|
||||
def: globalThis.def,
|
||||
use: globalThis.use,
|
||||
// use: name => ({ UIWindow, UIComponentWindow })[name],
|
||||
};
|
||||
globalThis.service_script_api_promise.resolve(service_script_api);
|
||||
|
||||
@ -78,7 +85,13 @@ const launch_services = async function () {
|
||||
}
|
||||
|
||||
for (const [_, instance] of services_l_) {
|
||||
await instance.init();
|
||||
await instance.construct();
|
||||
}
|
||||
|
||||
for (const [_, instance] of services_l_) {
|
||||
await instance.init({
|
||||
services: globalThis.services,
|
||||
});
|
||||
}
|
||||
|
||||
// === Service-Script Ready ===
|
||||
|
13
src/services/ExportRegistrantService.js
Normal file
13
src/services/ExportRegistrantService.js
Normal file
@ -0,0 +1,13 @@
|
||||
import Spinner from "../UI/Components/Spinner";
|
||||
import { Service } from "../definitions";
|
||||
|
||||
/**
|
||||
* This class exists to keep exports to the service script API separate
|
||||
* from the service where exports are registered. This will make it easier
|
||||
* to change how it works in the future.
|
||||
*/
|
||||
export class ExportRegistrantService extends Service {
|
||||
_init () {
|
||||
console.log(Spinner); // import gets optimized out if we don't do this
|
||||
}
|
||||
}
|
@ -44,6 +44,8 @@ const css_paths = [
|
||||
|
||||
// Ordered list of JS scripts
|
||||
const js_paths = [
|
||||
'/init_sync.js',
|
||||
'/init_async.js',
|
||||
'/initgui.js',
|
||||
'/helpers.js',
|
||||
'/IPC.js',
|
||||
|
@ -1,6 +1,8 @@
|
||||
import ValueHolder from "./ValueHolder.js";
|
||||
|
||||
export class Component extends HTMLElement {
|
||||
export const Component = def(class Component extends HTMLElement {
|
||||
static ID = 'util.Component';
|
||||
|
||||
#has_created_element = false;
|
||||
#has_called_on_ready = false;
|
||||
|
||||
@ -11,6 +13,27 @@ export class Component extends HTMLElement {
|
||||
'value bindings for create_template',
|
||||
]
|
||||
|
||||
static on_self_registered ({ is_owner, on_other_registered }) {
|
||||
// Only invoked for Component itself, not subclasses
|
||||
if ( ! is_owner ) return;
|
||||
|
||||
// Automatically define components for all HTML elements
|
||||
on_other_registered(({ cls }) => {
|
||||
console.log('detected class', cls.ID);
|
||||
if ( cls.ID === 'ui.component.StepHeading' ) {
|
||||
globalThis.sh_shouldbe = cls;
|
||||
console.log(
|
||||
'this is what StepHeading should be',
|
||||
cls
|
||||
);
|
||||
}
|
||||
if ( globalThis.lib.is_subclass(cls, HTMLElement) ) {
|
||||
console.log('registering as an element');
|
||||
defineComponent(cls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
constructor (property_values) {
|
||||
super();
|
||||
|
||||
@ -144,12 +167,21 @@ export class Component extends HTMLElement {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const defineComponent = (name, component) => {
|
||||
export const defineComponent = (component) => {
|
||||
// Web components need tags (despite that we never use the tags)
|
||||
// because it was designed this way.
|
||||
if ( globalThis.lib.is_subclass(component, HTMLElement) ) {
|
||||
let name = component.ID;
|
||||
name = 'c-' + name.split('.').pop().toLowerCase();
|
||||
// TODO: This is necessary because files can be loaded from
|
||||
// both `/src/UI` and `/UI` in the URL; we need to fix that
|
||||
if ( ! customElements.get(name) ) {
|
||||
if ( customElements.get(name) ) return;
|
||||
|
||||
// console.log('[surely] defining', name, 'as', component);
|
||||
|
||||
customElements.define(name, component);
|
||||
component.defined_as = name;
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,6 @@
|
||||
export default class TeePromise {
|
||||
export default def(class TeePromise {
|
||||
static ID = 'util.TeePromise';
|
||||
|
||||
static STATUS_PENDING = {};
|
||||
static STATUS_RUNNING = {};
|
||||
static STATUS_DONE = {};
|
||||
@ -40,4 +42,4 @@ export default class TeePromise {
|
||||
onComplete(fn) {
|
||||
return this.then(fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user