@omicronenergy/oscd-shell 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,36 +1,57 @@
1
1
  import '@webcomponents/scoped-custom-element-registry';
2
2
  import { LitElement, TemplateResult } from 'lit';
3
- import { OscdAppBar } from '@omicronenergy/oscd-ui/app-bar/OscdAppBar.js';
4
- import { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';
5
- import { OscdDivider } from '@omicronenergy/oscd-ui/divider/OscdDivider.js';
6
- import { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';
7
- import { OscdFilledSelect } from '@omicronenergy/oscd-ui/select/OscdFilledSelect.js';
8
- import { OscdFilledTextField } from '@omicronenergy/oscd-ui/textfield/OscdFilledTextField.js';
9
- import { OscdIcon } from '@omicronenergy/oscd-ui/icon/OscdIcon.js';
10
- import { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';
11
- import { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';
12
- import { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';
13
- import { OscdMenuItem } from '@omicronenergy/oscd-ui/menu/OscdMenuItem.js';
14
- import { OscdNavigationDrawer } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawer.js';
15
- import { OscdNavigationDrawerHeader } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawerHeader.js';
16
- import { OscdSecondaryTab } from '@omicronenergy/oscd-ui/tabs/OscdSecondaryTab.js';
17
- import { OscdSelectOption } from '@omicronenergy/oscd-ui/select/OscdSelectOption.js';
18
- import { OscdTabs } from '@omicronenergy/oscd-ui/tabs/OscdTabs.js';
19
- import { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';
3
+ import '@omicronenergy/oscd-ui/app-bar/oscd-app-bar.js';
4
+ import type { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';
5
+ import '@omicronenergy/oscd-ui/dialog/oscd-dialog.js';
6
+ import '@omicronenergy/oscd-ui/divider/oscd-divider.js';
7
+ import type { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';
8
+ import '@omicronenergy/oscd-ui/iconbutton/oscd-filled-icon-button.js';
9
+ import '@omicronenergy/oscd-ui/select/oscd-filled-select.js';
10
+ import '@omicronenergy/oscd-ui/textfield/oscd-filled-text-field.js';
11
+ import '@omicronenergy/oscd-ui/icon/oscd-icon.js';
12
+ import '@omicronenergy/oscd-ui/list/oscd-list.js';
13
+ import '@omicronenergy/oscd-ui/list/oscd-list-item.js';
14
+ import type { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';
15
+ import '@omicronenergy/oscd-ui/menu/oscd-menu.js';
16
+ import '@omicronenergy/oscd-ui/menu/oscd-menu-item.js';
17
+ import type { OscdNavigationDrawer } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawer.js';
18
+ import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer.js';
19
+ import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer-header.js';
20
+ import '@omicronenergy/oscd-ui/tabs/oscd-secondary-tab.js';
21
+ import '@omicronenergy/oscd-ui/select/oscd-select-option.js';
22
+ import '@omicronenergy/oscd-ui/tabs/oscd-tabs.js';
23
+ import '@omicronenergy/oscd-ui/button/oscd-text-button.js';
20
24
  import { XMLEditor } from '@omicronenergy/oscd-editor';
21
- import { OpenEvent } from '@omicronenergy/oscd-api';
25
+ import { EditV2, OpenEvent, Transactor } from '@omicronenergy/oscd-api';
22
26
  import { allLocales, targetLocales } from './locales.js';
23
- export type Plugin = {
27
+ export interface Plugin {
28
+ editor: Transactor<EditV2>;
29
+ docs: Record<string, XMLDocument>;
30
+ doc?: XMLDocument;
31
+ docName?: string;
32
+ stateVersion: unknown;
33
+ /** @deprecated Use `stateVersion` instead */
34
+ editCount: number;
35
+ locale: string;
36
+ }
37
+ export type PluginEntry = {
38
+ name: string;
39
+ translations?: Record<(typeof targetLocales)[number], string>;
40
+ tagName: string;
41
+ icon: string;
42
+ requireDoc?: boolean;
43
+ };
44
+ export type SourcedPluginEntry = {
24
45
  name: string;
25
46
  translations?: Record<(typeof targetLocales)[number], string>;
26
47
  src: string;
27
48
  icon: string;
28
49
  requireDoc?: boolean;
29
50
  };
30
- export type PluginSet = {
31
- menu: Plugin[];
32
- editor: Plugin[];
33
- background: Plugin[];
51
+ export type PluginSet<P = PluginEntry> = {
52
+ menu: P[];
53
+ editor: P[];
54
+ background: P[];
34
55
  };
35
56
  type Control = {
36
57
  icon: string;
@@ -42,45 +63,27 @@ type RenderedPlugin = Control & {
42
63
  tagName: string;
43
64
  };
44
65
  type LocaleTag = (typeof allLocales)[number];
45
- declare const OpenSCD_base: typeof LitElement & import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHostConstructor;
46
- export declare class OpenSCD extends OpenSCD_base {
66
+ export declare class OpenSCD extends LitElement {
47
67
  #private;
48
- static get scopedElements(): {
49
- 'oscd-app-bar': typeof OscdAppBar;
50
- 'oscd-dialog': typeof OscdDialog;
51
- 'oscd-icon': typeof OscdIcon;
52
- 'oscd-filled-text-field': typeof OscdFilledTextField;
53
- 'oscd-filled-select': typeof OscdFilledSelect;
54
- 'oscd-select-option': typeof OscdSelectOption;
55
- 'oscd-filled-icon-button': typeof OscdFilledIconButton;
56
- 'oscd-list': typeof OscdList;
57
- 'oscd-list-item': typeof OscdListItem;
58
- 'oscd-divider': typeof OscdDivider;
59
- 'oscd-menu-item': typeof OscdMenuItem;
60
- 'oscd-navigation-drawer': typeof OscdNavigationDrawer;
61
- 'oscd-navigation-drawer-header': typeof OscdNavigationDrawerHeader;
62
- 'oscd-secondary-tab': typeof OscdSecondaryTab;
63
- 'oscd-tabs': typeof OscdTabs;
64
- 'oscd-text-button': typeof OscdTextButton;
65
- 'oscd-menu': typeof OscdMenu;
66
- };
68
+ /** The file endings of editable files */
69
+ editable: string[];
70
+ isEditable(docName: string): boolean;
71
+ get editableDocs(): string[];
72
+ private _docs;
73
+ /** The set of `XMLDocument`s currently loaded */
74
+ get docs(): Record<string, XMLDocument>;
75
+ set docs(newDocs: Record<string, XMLDocument>);
76
+ onDocsChanged(): void;
77
+ /** The name of the [[`doc`]] currently being edited */
78
+ docName: string;
67
79
  get doc(): XMLDocument;
68
80
  xmlEditor: XMLEditor;
81
+ stateVersion: number;
69
82
  get last(): number;
70
83
  get canUndo(): boolean;
71
84
  get canRedo(): boolean;
72
- /** The set of `XMLDocument`s currently loaded */
73
- docs: Record<string, XMLDocument>;
74
- /** The name of the [[`doc`]] currently being edited */
75
- docName: string;
76
- /** The file endings of editable files */
77
- editable: string[];
78
- isEditable(docName: string): boolean;
79
- get editableDocs(): string[];
80
85
  get plugins(): PluginSet;
81
- set plugins(plugins: Partial<PluginSet>);
82
- get loadedPlugins(): PluginSet;
83
- addLoadedPlugin(tagName: string, kind: keyof PluginSet, plugin: Plugin, index: number): void;
86
+ set plugins(plugins: Partial<PluginSet<Partial<PluginEntry | SourcedPluginEntry>>>);
84
87
  handleOpenDoc({ detail: { docName, doc } }: OpenEvent): void;
85
88
  /** Undo the last `n` [[Edit]]s committed */
86
89
  undo(n?: number): void;
@@ -103,6 +106,7 @@ export declare class OpenSCD extends OpenSCD_base {
103
106
  private handleKeyPress;
104
107
  constructor();
105
108
  updated(changedProps: Map<string, unknown>): void;
109
+ renderPlugin(tagName: string): TemplateResult;
106
110
  render(): TemplateResult<1>;
107
111
  firstUpdated(): void;
108
112
  static styles: import("lit").CSSResult;
@@ -1,45 +1,36 @@
1
- var _OpenSCD_plugins, _OpenSCD_loadedPlugins, _OpenSCD_loadedPluginTagNames, _OpenSCD_actions;
1
+ var _OpenSCD_plugins, _OpenSCD_actions;
2
2
  import { __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib";
3
3
  import '@webcomponents/scoped-custom-element-registry';
4
4
  import { configureLocalization, localized, msg } from '@lit/localize';
5
5
  import { css, html, LitElement, nothing } from 'lit';
6
6
  import { customElement, property, query, state } from 'lit/decorators.js';
7
7
  import { html as staticHtml, unsafeStatic } from 'lit/static-html.js';
8
- import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
9
- import { OscdAppBar } from '@omicronenergy/oscd-ui/app-bar/OscdAppBar.js';
10
- import { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';
11
- import { OscdDivider } from '@omicronenergy/oscd-ui/divider/OscdDivider.js';
12
- import { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';
13
- import { OscdFilledSelect } from '@omicronenergy/oscd-ui/select/OscdFilledSelect.js';
14
- import { OscdFilledTextField } from '@omicronenergy/oscd-ui/textfield/OscdFilledTextField.js';
15
- import { OscdIcon } from '@omicronenergy/oscd-ui/icon/OscdIcon.js';
16
- import { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';
17
- import { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';
18
- import { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';
19
- import { OscdMenuItem } from '@omicronenergy/oscd-ui/menu/OscdMenuItem.js';
20
- import { OscdNavigationDrawer } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawer.js';
21
- import { OscdNavigationDrawerHeader } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawerHeader.js';
22
- import { OscdSecondaryTab } from '@omicronenergy/oscd-ui/tabs/OscdSecondaryTab.js';
23
- import { OscdSelectOption } from '@omicronenergy/oscd-ui/select/OscdSelectOption.js';
24
- import { OscdTabs } from '@omicronenergy/oscd-ui/tabs/OscdTabs.js';
25
- import { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';
8
+ import '@omicronenergy/oscd-ui/app-bar/oscd-app-bar.js';
9
+ import '@omicronenergy/oscd-ui/dialog/oscd-dialog.js';
10
+ import '@omicronenergy/oscd-ui/divider/oscd-divider.js';
11
+ import '@omicronenergy/oscd-ui/iconbutton/oscd-filled-icon-button.js';
12
+ import '@omicronenergy/oscd-ui/select/oscd-filled-select.js';
13
+ import '@omicronenergy/oscd-ui/textfield/oscd-filled-text-field.js';
14
+ import '@omicronenergy/oscd-ui/icon/oscd-icon.js';
15
+ import '@omicronenergy/oscd-ui/list/oscd-list.js';
16
+ import '@omicronenergy/oscd-ui/list/oscd-list-item.js';
17
+ import '@omicronenergy/oscd-ui/menu/oscd-menu.js';
18
+ import '@omicronenergy/oscd-ui/menu/oscd-menu-item.js';
19
+ import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer.js';
20
+ import '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer-header.js';
21
+ import '@omicronenergy/oscd-ui/tabs/oscd-secondary-tab.js';
22
+ import '@omicronenergy/oscd-ui/select/oscd-select-option.js';
23
+ import '@omicronenergy/oscd-ui/tabs/oscd-tabs.js';
24
+ import '@omicronenergy/oscd-ui/button/oscd-text-button.js';
26
25
  import { XMLEditor } from '@omicronenergy/oscd-editor';
27
26
  import { sourceLocale, targetLocales } from './locales.js';
28
- import { cyrb64 } from './foundation/cyrb64.js';
27
+ import { loadSourcedPlugins } from './utils/plugin-utils.js';
29
28
  const _customElementsDefine = window.customElements.define;
30
29
  window.customElements.define = (name, cl, conf) => {
31
30
  if (!customElements.get(name)) {
32
31
  _customElementsDefine.call(window.customElements, name, cl, conf);
33
32
  }
34
33
  };
35
- const pluginTags = new Map();
36
- /** @returns a valid customElement tagName containing the URI hash. */
37
- function pluginTag(uri) {
38
- if (!pluginTags.has(uri)) {
39
- pluginTags.set(uri, `oscd-p${cyrb64(uri)}`);
40
- }
41
- return pluginTags.get(uri);
42
- }
43
34
  const { getLocale, setLocale } = configureLocalization({
44
35
  sourceLocale,
45
36
  targetLocales,
@@ -66,27 +57,23 @@ function renderMenuItem(control) {
66
57
  </oscd-list-item>
67
58
  `;
68
59
  }
69
- let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
70
- static get scopedElements() {
71
- return {
72
- 'oscd-app-bar': OscdAppBar,
73
- 'oscd-dialog': OscdDialog,
74
- 'oscd-icon': OscdIcon,
75
- 'oscd-filled-text-field': OscdFilledTextField,
76
- 'oscd-filled-select': OscdFilledSelect,
77
- 'oscd-select-option': OscdSelectOption,
78
- 'oscd-filled-icon-button': OscdFilledIconButton,
79
- 'oscd-list': OscdList,
80
- 'oscd-list-item': OscdListItem,
81
- 'oscd-divider': OscdDivider,
82
- 'oscd-menu-item': OscdMenuItem,
83
- 'oscd-navigation-drawer': OscdNavigationDrawer,
84
- 'oscd-navigation-drawer-header': OscdNavigationDrawerHeader,
85
- 'oscd-secondary-tab': OscdSecondaryTab,
86
- 'oscd-tabs': OscdTabs,
87
- 'oscd-text-button': OscdTextButton,
88
- 'oscd-menu': OscdMenu,
89
- };
60
+ let OpenSCD = class OpenSCD extends LitElement {
61
+ isEditable(docName) {
62
+ return !!this.editable.find(ext => docName.toLowerCase().endsWith(`.${ext}`));
63
+ }
64
+ get editableDocs() {
65
+ return Object.keys(this.docs).filter(name => this.isEditable(name));
66
+ }
67
+ /** The set of `XMLDocument`s currently loaded */
68
+ get docs() {
69
+ return this._docs;
70
+ }
71
+ set docs(newDocs) {
72
+ this._docs = newDocs;
73
+ this.onDocsChanged();
74
+ }
75
+ onDocsChanged() {
76
+ this.stateVersion += 1;
90
77
  }
91
78
  get doc() {
92
79
  return this.docs[this.docName];
@@ -100,47 +87,21 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
100
87
  get canRedo() {
101
88
  return this.xmlEditor.future.length >= 0;
102
89
  }
103
- isEditable(docName) {
104
- return !!this.editable.find(ext => docName.toLowerCase().endsWith(`.${ext}`));
105
- }
106
- get editableDocs() {
107
- return Object.keys(this.docs).filter(name => this.isEditable(name));
108
- }
109
90
  get plugins() {
110
91
  return __classPrivateFieldGet(this, _OpenSCD_plugins, "f");
111
92
  }
112
93
  set plugins(plugins) {
113
- Object.entries(plugins).forEach(([pluginType, kind]) => kind.forEach((plugin, index) => {
114
- const tagName = pluginTag(plugin.src);
115
- if (__classPrivateFieldGet(this, _OpenSCD_loadedPluginTagNames, "f").includes(tagName)) {
116
- return;
117
- }
118
- if (this.registry.get(tagName)) {
119
- this.addLoadedPlugin(tagName, pluginType, plugin, index);
120
- this.requestUpdate('loadedPlugins');
121
- return;
122
- }
123
- const url = new URL(plugin.src, window.location.href).toString();
124
- import(url).then(mod => {
125
- this.addLoadedPlugin(tagName, pluginType, plugin, index);
126
- // Because this is async, we need to check (again) if the element is already defined.
127
- if (!this.registry.get(tagName)) {
128
- this.registry.define(tagName, mod.default);
129
- }
130
- this.requestUpdate('loadedPlugins');
131
- });
132
- }));
133
- __classPrivateFieldSet(this, _OpenSCD_plugins, { menu: [], editor: [], background: [], ...plugins }, "f");
134
- }
135
- get loadedPlugins() {
136
- return __classPrivateFieldGet(this, _OpenSCD_loadedPlugins, "f");
137
- }
138
- addLoadedPlugin(tagName, kind, plugin, index) {
139
- __classPrivateFieldGet(this, _OpenSCD_loadedPlugins, "f")[kind][index] = plugin;
140
- __classPrivateFieldGet(this, _OpenSCD_loadedPluginTagNames, "f").push(tagName);
94
+ __classPrivateFieldSet(this, _OpenSCD_plugins, Object.entries(plugins).reduce((acc, [pluginType, kind]) => {
95
+ const convertedPlugins = loadSourcedPlugins(kind);
96
+ acc[pluginType] = convertedPlugins;
97
+ return acc;
98
+ }, { menu: [], editor: [], background: [] }), "f");
141
99
  }
142
100
  handleOpenDoc({ detail: { docName, doc } }) {
143
- this.docs[docName] = doc;
101
+ this.docs = {
102
+ ...this.docs,
103
+ [docName]: doc,
104
+ };
144
105
  if (this.isEditable(docName)) {
145
106
  this.docName = docName;
146
107
  }
@@ -183,25 +144,29 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
183
144
  return this.editors[this.editorIndex]?.tagName ?? '';
184
145
  }
185
146
  get menu() {
186
- return (this.loadedPlugins.menu
187
- ?.map((plugin) => ({
188
- icon: plugin.icon,
189
- getName: () => plugin.translations?.[this.locale] || plugin.name,
190
- isDisabled: () => (plugin.requireDoc && !this.docName) ?? false,
191
- tagName: pluginTag(plugin.src),
147
+ return (this.plugins.menu
148
+ ?.map(({ name, tagName, requireDoc, icon, translations }) => ({
149
+ icon,
150
+ getName: () => translations?.[this.locale] ||
151
+ name,
152
+ isDisabled: () => (requireDoc && !this.docName) ?? false,
153
+ tagName,
192
154
  action: () => {
193
- this.shadowRoot.querySelector(pluginTag(plugin.src)).run?.();
194
- this.menuUI.opened = false;
155
+ if (tagName) {
156
+ this.shadowRoot.querySelector(tagName).run?.();
157
+ this.menuUI.opened = false;
158
+ }
195
159
  },
196
160
  }))
197
161
  .filter(p => p !== undefined)).concat(__classPrivateFieldGet(this, _OpenSCD_actions, "f"));
198
162
  }
199
163
  get editors() {
200
- return this.loadedPlugins.editor?.map((plugin) => ({
201
- icon: plugin.icon,
202
- getName: () => plugin.translations?.[this.locale] || plugin.name,
203
- isDisabled: () => (plugin.requireDoc && !this.docName) ?? false,
204
- tagName: pluginTag(plugin.src),
164
+ return this.plugins.editor?.map(({ name, tagName, requireDoc, icon, translations, }) => ({
165
+ icon,
166
+ getName: () => translations?.[this.locale] ||
167
+ name,
168
+ isDisabled: () => (requireDoc && !this.docName) ?? false,
169
+ tagName,
205
170
  }));
206
171
  }
207
172
  handleKeyPress(e) {
@@ -216,11 +181,6 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
216
181
  }
217
182
  constructor() {
218
183
  super();
219
- this.xmlEditor = new XMLEditor();
220
- /** The set of `XMLDocument`s currently loaded */
221
- this.docs = {};
222
- /** The name of the [[`doc`]] currently being edited */
223
- this.docName = '';
224
184
  /** The file endings of editable files */
225
185
  this.editable = [
226
186
  'cid',
@@ -230,13 +190,12 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
230
190
  'sed',
231
191
  'ssd',
232
192
  ];
193
+ this._docs = {};
194
+ /** The name of the [[`doc`]] currently being edited */
195
+ this.docName = '';
196
+ this.xmlEditor = new XMLEditor();
197
+ this.stateVersion = -1;
233
198
  _OpenSCD_plugins.set(this, { menu: [], editor: [], background: [] });
234
- _OpenSCD_loadedPlugins.set(this, {
235
- menu: new Array(this.plugins.menu.length).fill(null),
236
- editor: new Array(this.plugins.editor.length).fill(null),
237
- background: new Array(this.plugins.editor.length).fill(null),
238
- });
239
- _OpenSCD_loadedPluginTagNames.set(this, []);
240
199
  this.editorIndex = 0;
241
200
  this.controls = {
242
201
  undo: {
@@ -284,7 +243,7 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
284
243
  });
285
244
  // Catch all edits (from commits AND events) and trigger an update
286
245
  this.xmlEditor.subscribe(() => {
287
- this.requestUpdate();
246
+ this.stateVersion += 1;
288
247
  });
289
248
  }
290
249
  updated(changedProps) {
@@ -297,6 +256,18 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
297
256
  }
298
257
  });
299
258
  }
259
+ renderPlugin(tagName) {
260
+ const tag = unsafeStatic(tagName);
261
+ return staticHtml `<${tag}
262
+ .locale="${this.locale}"
263
+ .docName="${this.docName}"
264
+ .doc=${this.doc}
265
+ .docs=${this.docs}
266
+ .editCount=${this.stateVersion}
267
+ .stateVersion=${this.stateVersion}
268
+ .editor=${this.xmlEditor}>
269
+ </${tag}>`;
270
+ }
300
271
  render() {
301
272
  return html `<oscd-app-bar slot="appContent">
302
273
  ${renderActionItem(this.controls.menu, 'actionStart')}
@@ -366,15 +337,7 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
366
337
  </oscd-list>
367
338
  </oscd-navigation-drawer>
368
339
 
369
- ${this.editor
370
- ? staticHtml `<${unsafeStatic(this.editor)} locale="${this.locale}"
371
- docName="${this.docName}"
372
- .doc=${this.doc}
373
- .docs=${this.docs}
374
- .editCount=${this.xmlEditor.past.length}
375
- .editor=${this.xmlEditor}>
376
- </${unsafeStatic(this.editor)}>`
377
- : nothing}
340
+ ${this.editor ? this.renderPlugin(this.editor) : nothing}
378
341
 
379
342
  <oscd-dialog
380
343
  id="editFile"
@@ -440,8 +403,12 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
440
403
  return;
441
404
  }
442
405
  const newDocName = `${this.fileNameUI.value}.${this.fileExtensionUI.value}`;
443
- this.docs[newDocName] = this.doc;
406
+ const doc = this.docs[this.docName];
444
407
  delete this.docs[this.docName];
408
+ this.docs = {
409
+ ...this.docs,
410
+ [newDocName]: doc,
411
+ };
445
412
  this.docName = newDocName;
446
413
  this.editFileUI.close();
447
414
  }}
@@ -455,24 +422,12 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
455
422
 
456
423
  <aside>
457
424
  <div class="menu-plugins">
458
- ${this.loadedPlugins.menu.map(plugin => staticHtml `<${unsafeStatic(pluginTag(plugin.src))}
459
- locale="${this.locale}"
460
- docName="${this.docName}"
461
- .doc=${this.doc}
462
- .docs=${this.docs}
463
- .editCount=${this.xmlEditor.past.length}
464
- .editor=${this.xmlEditor}>
465
- </${unsafeStatic(pluginTag(plugin.src))}>`)}
425
+ ${this.plugins.menu.map(plugin => this.renderPlugin(plugin.tagName))}
466
426
  </div>
467
427
  <div class="background-plugins">
468
- ${this.loadedPlugins.background.map(plugin => staticHtml `<${unsafeStatic(pluginTag(plugin.src))}
469
- locale="${this.locale}"
470
- docName="${this.docName}"
471
- .doc=${this.doc}
472
- .docs=${this.docs}
473
- .editCount=${this.xmlEditor.past.length}
474
- .editor=${this.xmlEditor}>
475
- </${unsafeStatic(pluginTag(plugin.src))}>`)}
428
+ ${this.plugins.background
429
+ .filter(plugin => !plugin.requireDoc || !!this.docName)
430
+ .map(plugin => this.renderPlugin(plugin.tagName))}
476
431
  </div>
477
432
  </aside> `;
478
433
  }
@@ -482,8 +437,6 @@ let OpenSCD = class OpenSCD extends ScopedElementsMixin(LitElement) {
482
437
  }
483
438
  };
484
439
  _OpenSCD_plugins = new WeakMap();
485
- _OpenSCD_loadedPlugins = new WeakMap();
486
- _OpenSCD_loadedPluginTagNames = new WeakMap();
487
440
  _OpenSCD_actions = new WeakMap();
488
441
  OpenSCD.styles = css `
489
442
  .fileext {
@@ -657,6 +610,18 @@ OpenSCD.styles = css `
657
610
  position: relative;
658
611
  }
659
612
  `;
613
+ __decorate([
614
+ property({ type: Array, reflect: true })
615
+ ], OpenSCD.prototype, "editable", void 0);
616
+ __decorate([
617
+ state()
618
+ ], OpenSCD.prototype, "editableDocs", null);
619
+ __decorate([
620
+ state()
621
+ ], OpenSCD.prototype, "docs", null);
622
+ __decorate([
623
+ property({ type: String, reflect: true })
624
+ ], OpenSCD.prototype, "docName", void 0);
660
625
  __decorate([
661
626
  state()
662
627
  /** The `XMLDocument` currently being edited */
@@ -664,6 +629,9 @@ __decorate([
664
629
  __decorate([
665
630
  state()
666
631
  ], OpenSCD.prototype, "xmlEditor", void 0);
632
+ __decorate([
633
+ state()
634
+ ], OpenSCD.prototype, "stateVersion", void 0);
667
635
  __decorate([
668
636
  state()
669
637
  ], OpenSCD.prototype, "last", null);
@@ -673,24 +641,9 @@ __decorate([
673
641
  __decorate([
674
642
  state()
675
643
  ], OpenSCD.prototype, "canRedo", null);
676
- __decorate([
677
- state()
678
- ], OpenSCD.prototype, "docs", void 0);
679
- __decorate([
680
- property({ type: String, reflect: true })
681
- ], OpenSCD.prototype, "docName", void 0);
682
- __decorate([
683
- property({ type: Array, reflect: true })
684
- ], OpenSCD.prototype, "editable", void 0);
685
- __decorate([
686
- state()
687
- ], OpenSCD.prototype, "editableDocs", null);
688
644
  __decorate([
689
645
  property({ type: Object })
690
646
  ], OpenSCD.prototype, "plugins", null);
691
- __decorate([
692
- state()
693
- ], OpenSCD.prototype, "loadedPlugins", null);
694
647
  __decorate([
695
648
  query('#editFile')
696
649
  ], OpenSCD.prototype, "editFileUI", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"oscd-shell.js","sourceRoot":"","sources":["../oscd-shell.ts"],"names":[],"mappings":"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","sourcesContent":["import '@webcomponents/scoped-custom-element-registry';\n\nimport { configureLocalization, localized, msg } from '@lit/localize';\nimport { css, html, LitElement, nothing, TemplateResult } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';\n\nimport { OscdAppBar } from '@omicronenergy/oscd-ui/app-bar/OscdAppBar.js';\nimport { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';\nimport { OscdDivider } from '@omicronenergy/oscd-ui/divider/OscdDivider.js';\nimport { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';\nimport { OscdFilledSelect } from '@omicronenergy/oscd-ui/select/OscdFilledSelect.js';\nimport { OscdFilledTextField } from '@omicronenergy/oscd-ui/textfield/OscdFilledTextField.js';\nimport { OscdIcon } from '@omicronenergy/oscd-ui/icon/OscdIcon.js';\nimport { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';\nimport { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';\nimport { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';\nimport { OscdMenuItem } from '@omicronenergy/oscd-ui/menu/OscdMenuItem.js';\nimport { OscdNavigationDrawer } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawer.js';\nimport { OscdNavigationDrawerHeader } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawerHeader.js';\nimport { OscdSecondaryTab } from '@omicronenergy/oscd-ui/tabs/OscdSecondaryTab.js';\nimport { OscdSelectOption } from '@omicronenergy/oscd-ui/select/OscdSelectOption.js';\nimport { OscdTabs } from '@omicronenergy/oscd-ui/tabs/OscdTabs.js';\nimport { OscdTextButton } from '@omicronenergy/oscd-ui/button/OscdTextButton.js';\n\nimport { XMLEditor } from '@omicronenergy/oscd-editor';\n\nimport { EditEventV2, OpenEvent } from '@omicronenergy/oscd-api';\n\nimport { allLocales, sourceLocale, targetLocales } from './locales.js';\n\nimport { cyrb64 } from './foundation/cyrb64.js';\n\nconst _customElementsDefine = window.customElements.define;\nwindow.customElements.define = (name, cl, conf) => {\n if (!customElements.get(name)) {\n _customElementsDefine.call(window.customElements, name, cl, conf);\n }\n};\n\nexport type Plugin = {\n name: string;\n translations?: Record<(typeof targetLocales)[number], string>;\n src: string;\n icon: string;\n requireDoc?: boolean;\n};\nexport type PluginSet = {\n menu: Plugin[];\n editor: Plugin[];\n background: Plugin[];\n};\n\nconst pluginTags = new Map<string, string>();\n\n/** @returns a valid customElement tagName containing the URI hash. */\nfunction pluginTag(uri: string): string {\n if (!pluginTags.has(uri)) {\n pluginTags.set(uri, `oscd-p${cyrb64(uri)}`);\n }\n return pluginTags.get(uri)!;\n}\n\ntype Control = {\n icon: string;\n getName: () => string;\n isDisabled: () => boolean;\n action?: () => void | Promise<void>;\n};\n\ntype RenderedPlugin = Control & { tagName: string };\n\ntype LocaleTag = (typeof allLocales)[number];\n\nconst { getLocale, setLocale } = configureLocalization({\n sourceLocale,\n targetLocales,\n loadLocale: locale =>\n import(new URL(`./locales/${locale}.js`, import.meta.url).href),\n});\n\nfunction renderActionItem(\n control: Control,\n slot = 'actionItems',\n): TemplateResult {\n return html`<oscd-filled-icon-button\n slot=\"${slot}\"\n aria-label=\"${control.getName()}\"\n ?disabled=${control.isDisabled()}\n @click=${control.action}\n ><oscd-icon>${control.icon}</oscd-icon></oscd-filled-icon-button\n >`;\n}\n\nfunction renderMenuItem(control: Control): TemplateResult {\n return html`\n <oscd-list-item\n type=\"button\"\n .disabled=${control.isDisabled()}\n @click=${control.action}\n >\n <oscd-icon slot=\"start\">${control.icon}</oscd-icon>\n <span>${control.getName()}</span>\n </oscd-list-item>\n `;\n}\n\n@customElement('oscd-shell')\n@localized()\nexport class OpenSCD extends ScopedElementsMixin(LitElement) {\n static get scopedElements() {\n return {\n 'oscd-app-bar': OscdAppBar,\n 'oscd-dialog': OscdDialog,\n 'oscd-icon': OscdIcon,\n 'oscd-filled-text-field': OscdFilledTextField,\n 'oscd-filled-select': OscdFilledSelect,\n 'oscd-select-option': OscdSelectOption,\n 'oscd-filled-icon-button': OscdFilledIconButton,\n 'oscd-list': OscdList,\n 'oscd-list-item': OscdListItem,\n 'oscd-divider': OscdDivider,\n 'oscd-menu-item': OscdMenuItem,\n 'oscd-navigation-drawer': OscdNavigationDrawer,\n 'oscd-navigation-drawer-header': OscdNavigationDrawerHeader,\n 'oscd-secondary-tab': OscdSecondaryTab,\n 'oscd-tabs': OscdTabs,\n 'oscd-text-button': OscdTextButton,\n 'oscd-menu': OscdMenu,\n };\n }\n\n @state()\n /** The `XMLDocument` currently being edited */\n get doc(): XMLDocument {\n return this.docs[this.docName];\n }\n\n @state()\n xmlEditor: XMLEditor = new XMLEditor();\n\n @state()\n get last(): number {\n return this.xmlEditor.past.length - 1;\n }\n\n @state()\n get canUndo(): boolean {\n return this.xmlEditor.past.length >= 0;\n }\n\n @state()\n get canRedo(): boolean {\n return this.xmlEditor.future.length >= 0;\n }\n\n /** The set of `XMLDocument`s currently loaded */\n @state()\n docs: Record<string, XMLDocument> = {};\n\n /** The name of the [[`doc`]] currently being edited */\n @property({ type: String, reflect: true }) docName = '';\n\n /** The file endings of editable files */\n @property({ type: Array, reflect: true }) editable = [\n 'cid',\n 'icd',\n 'iid',\n 'scd',\n 'sed',\n 'ssd',\n ];\n\n isEditable(docName: string): boolean {\n return !!this.editable.find(ext =>\n docName.toLowerCase().endsWith(`.${ext}`),\n );\n }\n\n @state()\n get editableDocs(): string[] {\n return Object.keys(this.docs).filter(name => this.isEditable(name));\n }\n\n #plugins: PluginSet = { menu: [], editor: [], background: [] };\n\n @property({ type: Object })\n get plugins(): PluginSet {\n return this.#plugins;\n }\n\n set plugins(plugins: Partial<PluginSet>) {\n Object.entries(plugins).forEach(([pluginType, kind]) =>\n kind.forEach((plugin, index) => {\n const tagName = pluginTag(plugin.src);\n if (this.#loadedPluginTagNames.includes(tagName)) {\n return;\n }\n\n if (this.registry!.get(tagName)) {\n this.addLoadedPlugin(\n tagName,\n pluginType as keyof PluginSet,\n plugin,\n index,\n );\n this.requestUpdate('loadedPlugins');\n return;\n }\n const url = new URL(plugin.src, window.location.href).toString();\n import(url).then(mod => {\n this.addLoadedPlugin(\n tagName,\n pluginType as keyof PluginSet,\n plugin,\n index,\n );\n // Because this is async, we need to check (again) if the element is already defined.\n if (!this.registry!.get(tagName)) {\n this.registry!.define(tagName, mod.default);\n }\n this.requestUpdate('loadedPlugins');\n });\n }),\n );\n this.#plugins = { menu: [], editor: [], background: [], ...plugins };\n }\n\n #loadedPlugins: PluginSet = {\n menu: new Array(this.plugins.menu.length).fill(null),\n editor: new Array(this.plugins.editor.length).fill(null),\n background: new Array(this.plugins.editor.length).fill(null),\n };\n\n #loadedPluginTagNames: string[] = [];\n\n @state()\n get loadedPlugins(): PluginSet {\n return this.#loadedPlugins;\n }\n\n addLoadedPlugin(\n tagName: string,\n kind: keyof PluginSet,\n plugin: Plugin,\n index: number,\n ) {\n this.#loadedPlugins[kind][index] = plugin;\n this.#loadedPluginTagNames.push(tagName);\n }\n\n handleOpenDoc({ detail: { docName, doc } }: OpenEvent) {\n this.docs[docName] = doc;\n if (this.isEditable(docName)) {\n this.docName = docName;\n }\n this.requestUpdate();\n }\n\n /** Undo the last `n` [[Edit]]s committed */\n undo(n = 1) {\n if (!this.canUndo || n < 1) {\n return;\n }\n this.xmlEditor.undo();\n if (n > 1) {\n this.undo(n - 1);\n }\n this.requestUpdate();\n }\n\n /** Redo the last `n` [[Edit]]s that have been undone */\n redo(n = 1) {\n if (!this.canRedo || n < 1) {\n return;\n }\n this.xmlEditor.redo();\n if (n > 1) {\n this.redo(n - 1);\n }\n this.requestUpdate();\n }\n\n @query('#editFile')\n editFileUI!: OscdDialog;\n\n @query('#menu')\n menuUI!: OscdNavigationDrawer;\n\n @query('#fileName')\n fileNameUI!: HTMLInputElement;\n\n @query('#fileExtension')\n fileExtensionUI!: HTMLInputElement;\n\n @query('#fileMenu')\n fileMenuUI!: OscdMenu;\n\n @query('#fileMenuButton')\n fileMenuButtonUI?: OscdFilledIconButton;\n\n @property({ type: String, reflect: true })\n get locale() {\n return getLocale() as LocaleTag;\n }\n\n set locale(tag: LocaleTag) {\n try {\n setLocale(tag);\n } catch {\n // don't change locale if tag is invalid\n }\n }\n\n @state()\n private editorIndex = 0;\n\n @state()\n get editor() {\n return this.editors[this.editorIndex]?.tagName ?? '';\n }\n\n private controls: Record<'undo' | 'redo' | 'menu', Required<Control>> = {\n undo: {\n icon: 'undo',\n getName: () => msg('Undo'),\n action: () => {\n this.undo();\n this.menuUI.opened = false;\n },\n isDisabled: () => !this.canUndo,\n },\n redo: {\n icon: 'redo',\n getName: () => msg('Redo'),\n action: () => {\n this.redo();\n this.menuUI.opened = false;\n },\n isDisabled: () => !this.canRedo,\n },\n\n menu: {\n icon: 'menu',\n getName: () => msg('Menu'),\n action: async () => {\n this.menuUI.opened = !this.menuUI.opened;\n await this.menuUI.updateComplete;\n if (this.menuUI.opened) {\n this.menuUI.querySelector<OscdList>('oscd-list')!.focus();\n }\n },\n isDisabled: () => false,\n },\n };\n\n #actions = [this.controls.undo, this.controls.redo];\n\n @state()\n get menu() {\n return (<Required<Control>[]>this.loadedPlugins.menu\n ?.map(\n (plugin): RenderedPlugin =>\n ({\n icon: plugin.icon,\n getName: () =>\n plugin.translations?.[\n this.locale as (typeof targetLocales)[number]\n ] || plugin.name,\n isDisabled: () => (plugin.requireDoc && !this.docName) ?? false,\n tagName: pluginTag(plugin.src),\n action: () => {\n this.shadowRoot!.querySelector<\n HTMLElement & { run: () => Promise<void> }\n >(pluginTag(plugin.src))!.run?.();\n this.menuUI.opened = false;\n },\n }) as RenderedPlugin,\n )\n .filter(p => p !== undefined)).concat(this.#actions);\n }\n\n @state()\n get editors() {\n return <RenderedPlugin[]>this.loadedPlugins.editor?.map(\n (plugin): RenderedPlugin | undefined =>\n ({\n icon: plugin.icon,\n getName: () =>\n plugin.translations?.[\n this.locale as (typeof targetLocales)[number]\n ] || plugin.name,\n isDisabled: () => (plugin.requireDoc && !this.docName) ?? false,\n tagName: pluginTag(plugin.src),\n }) as RenderedPlugin,\n );\n }\n\n private hotkeys: Partial<Record<string, () => void>> = {\n m: this.controls.menu.action,\n z: this.controls.undo.action,\n y: this.controls.redo.action,\n Z: this.controls.redo.action,\n };\n\n private handleKeyPress(e: KeyboardEvent): void {\n if (!e.ctrlKey) {\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(this.hotkeys, e.key)) {\n return;\n }\n this.hotkeys[e.key]!();\n e.preventDefault();\n }\n\n constructor() {\n super();\n\n document.addEventListener('keydown', event => this.handleKeyPress(event));\n\n this.addEventListener('oscd-open', event => this.handleOpenDoc(event));\n this.addEventListener('oscd-edit-v2', (event: EditEventV2) => {\n this.xmlEditor.commit(event.detail.edit);\n });\n\n // Catch all edits (from commits AND events) and trigger an update\n this.xmlEditor.subscribe(() => {\n this.requestUpdate();\n });\n }\n\n updated(changedProps: Map<string, unknown>) {\n super.updated(changedProps);\n\n this.updateComplete.then(() => {\n // Ensure the active tab is set after tabs are rendered\n const oscdTabs = this.shadowRoot!.querySelector('oscd-tabs');\n if (oscdTabs && oscdTabs.activeTabIndex !== this.editorIndex) {\n oscdTabs.activeTabIndex = this.editorIndex;\n }\n });\n }\n\n render() {\n return html`<oscd-app-bar slot=\"appContent\">\n ${renderActionItem(this.controls.menu, 'actionStart')}\n <div slot=\"title\" id=\"title\">\n ${this.editableDocs.length > 1\n ? html`<oscd-filled-icon-button\n id=\"fileMenuButton\"\n @click=${() => this.fileMenuUI.show()}\n ><oscd-icon>arrow_drop_down</oscd-icon></oscd-filled-icon-button\n >`\n : nothing}\n ${this.docName}\n ${this.docName\n ? html`<oscd-filled-icon-button\n @click=${() => this.editFileUI.show()}\n ><oscd-icon>edit</oscd-icon></oscd-filled-icon-button\n >`\n : nothing}\n <oscd-menu\n fixed\n id=\"fileMenu\"\n anchor=\"fileMenuButton\"\n corner=\"BOTTOM_END\"\n >\n ${this.editableDocs.map(\n (name, index) =>\n html`<oscd-menu-item\n @click=${() => {\n this.docName = name;\n }}\n ?disabled=${!this.isEditable(name)}\n ?selected=${this.docName === name}\n id=${index}\n >${name}</oscd-menu-item\n >`,\n )}\n </oscd-menu>\n </div>\n\n <div slot=\"actionEnd\">\n ${this.#actions.map(op => renderActionItem(op))}\n </div>\n\n <oscd-tabs\n .activeTabIndex=${this.editorIndex}\n @change=${(event: Event) => {\n const tabs = event.currentTarget as OscdTabs;\n this.editorIndex = tabs.activeTabIndex;\n }}\n >\n ${this.editors.map(editor =>\n editor.isDisabled()\n ? nothing\n : html`<oscd-secondary-tab>\n <oscd-icon slot=\"icon\">${editor.icon}</oscd-icon>\n ${editor.getName()}\n </oscd-secondary-tab>`,\n )}\n </oscd-tabs>\n </oscd-app-bar>\n\n <oscd-navigation-drawer hasheader type=\"modal\" id=\"menu\">\n <oscd-navigation-drawer-header>\n <span slot=\"headline\">${msg('Menu')}</span>\n ${this.docName\n ? html`<span slot=\"supporting-text\">${this.docName}</span>`\n : ''}\n </oscd-navigation-drawer-header>\n <oscd-list>\n <oscd-divider></oscd-divider>\n ${this.menu.map(renderMenuItem)}\n </oscd-list>\n </oscd-navigation-drawer>\n\n ${this.editor\n ? staticHtml`<${unsafeStatic(this.editor)} locale=\"${this.locale}\"\n docName=\"${this.docName}\"\n .doc=${this.doc}\n .docs=${this.docs} \n .editCount=${this.xmlEditor.past.length}\n .editor=${this.xmlEditor}>\n </${unsafeStatic(this.editor)}>`\n : nothing}\n\n <oscd-dialog\n id=\"editFile\"\n @closed=${(event: CustomEvent) => {\n const dialog = event.target as OscdDialog;\n if (dialog.returnValue === 'remove') {\n delete this.docs[this.docName];\n this.docName = this.editableDocs[0] || '';\n }\n }}\n >\n <div slot=\"headline\">\n <oscd-icon>description</oscd-icon>${this.docName}\n </div>\n <form slot=\"content\" id=\"edit-file-form\" method=\"dialog\">\n <oscd-filled-text-field\n id=\"fileName\"\n label=\"${msg('Filename')}\"\n value=\"${this.docName.replace(/\\.[^.]+$/, '')}\"\n @input=${(event: Event) => {\n const input = event.target as HTMLInputElement;\n const { value } = input;\n const name = `${value}.${this.fileExtensionUI.value}`;\n if (name in this.docs && name !== this.docName) {\n input.setCustomValidity('File already exists!');\n } else {\n input.setCustomValidity('');\n }\n input.reportValidity();\n }}\n ></oscd-filled-text-field>\n <oscd-filled-select\n label=\"${msg('Extension')}\"\n fixedMenuPosition\n id=\"fileExtension\"\n @selected=${() => this.fileNameUI.reportValidity()}\n >\n ${this.editable.map(\n ext =>\n html`<oscd-select-option\n ?selected=${this.docName.endsWith(`.${ext}`)}\n value=\"${ext}\"\n >${ext}</oscd-select-option\n >`,\n )}\n </oscd-filled-select>\n </form>\n <div slot=\"actions\">\n <oscd-text-button\n form=\"edit-file-form\"\n class=\"edit-dialog-remove-button\"\n value=\"remove\"\n >\n <oscd-icon slot=\"icon\">delete</oscd-icon>\n ${msg('Close file')}\n </oscd-text-button>\n <oscd-text-button form=\"edit-file-form\" value=\"close\">\n ${msg('Cancel')}\n </oscd-text-button>\n <oscd-text-button\n @click=${() => {\n const valid = this.fileNameUI.checkValidity();\n if (!valid) {\n this.fileNameUI.reportValidity();\n return;\n }\n const newDocName = `${this.fileNameUI.value}.${this.fileExtensionUI.value}`;\n this.docs[newDocName] = this.doc;\n delete this.docs[this.docName];\n this.docName = newDocName;\n this.editFileUI.close();\n }}\n trailing-icon\n >\n <oscd-icon slot=\"icon\">edit</oscd-icon>\n ${msg('Rename')}\n </oscd-text-button>\n </div>\n </oscd-dialog>\n\n <aside>\n <div class=\"menu-plugins\">\n ${this.loadedPlugins.menu.map(\n plugin =>\n staticHtml`<${unsafeStatic(pluginTag(plugin.src))} \n locale=\"${this.locale}\"\n docName=\"${this.docName}\"\n .doc=${this.doc}\n .docs=${this.docs} \n .editCount=${this.xmlEditor.past.length}\n .editor=${this.xmlEditor}>\n </${unsafeStatic(pluginTag(plugin.src))}>`,\n )}\n </div>\n <div class=\"background-plugins\">\n ${this.loadedPlugins.background.map(\n plugin =>\n staticHtml`<${unsafeStatic(pluginTag(plugin.src))} \n locale=\"${this.locale}\"\n docName=\"${this.docName}\"\n .doc=${this.doc}\n .docs=${this.docs} \n .editCount=${this.xmlEditor.past.length}\n .editor=${this.xmlEditor}>\n </${unsafeStatic(pluginTag(plugin.src))}>`,\n )}\n </div>\n </aside> `;\n }\n\n firstUpdated() {\n const background = getComputedStyle(this.menuUI).getPropertyValue(\n '--oscd-base2',\n );\n document.body.style.background = background;\n }\n\n static styles = css`\n .fileext {\n opacity: 0.81;\n }\n\n .filename {\n caret-color: var(--oscd-secondary);\n }\n\n .filename:focus {\n outline: none;\n }\n\n aside {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n overflow: hidden;\n margin: 0;\n padding: 0;\n }\n\n abbr {\n text-decoration: none;\n }\n\n @media (prefers-color-scheme: light) {\n * {\n --oscd-primary: var(--oscd-theme-primary, #2aa198);\n --oscd-secondary: var(--oscd-theme-secondary, #6c71c4);\n --oscd-base03: var(--oscd-theme-base03, #002b36);\n --oscd-base02: var(--oscd-theme-base02, #073642);\n --oscd-base01: var(--oscd-theme-base01, #586e75);\n --oscd-base00: var(--oscd-theme-base00, #657b83);\n --oscd-base0: var(--oscd-theme-base0, #839496);\n --oscd-base1: var(--oscd-theme-base1, #93a1a1);\n --oscd-base2: var(--oscd-theme-base2, #eee8d5);\n --oscd-base3: var(--oscd-theme-base3, #fdf6e3);\n\n --mdc-theme-text-disabled-on-light: rgba(255, 255, 255, 0.38);\n }\n }\n\n @media (prefers-color-scheme: dark) {\n * {\n --oscd-primary: var(--oscd-theme-secondary, #6c71c4);\n --oscd-secondary: var(--oscd-theme-primary, #2aa198);\n --oscd-base03: var(--oscd-theme-base3, #fdf6e3);\n --oscd-base02: var(--oscd-theme-base2, #eee8d5);\n --oscd-base01: var(--oscd-theme-base1, #93a1a1);\n --oscd-base00: var(--oscd-theme-base0, #839496);\n --oscd-base0: var(--oscd-theme-base00, #657b83);\n --oscd-base1: var(--oscd-theme-base01, #586e75);\n --oscd-base2: var(--oscd-theme-base02, #073642);\n --oscd-base3: var(--oscd-theme-base03, #002b36);\n }\n }\n\n * {\n --oscd-error: var(--oscd-theme-error, #dc322f);\n\n --oscd-text-font: var(--oscd-theme-text-font, 'Roboto');\n --oscd-icon-font: var(--oscd-theme-icon-font, 'Material Icons');\n --oscd-text-font-mono: var(--oscd-theme-text-font-mono, 'Roboto Mono');\n\n --oscd-warning: var(--oscd-theme-warning, #b58900);\n --md-sys-color-primary: var(--oscd-primary);\n --md-sys-color-on-primary: var(--oscd-base3);\n\n --md-sys-color-secondary: var(--oscd-secondary);\n --md-sys-color-on-secondary: var(--oscd-base3);\n --md-sys-color-secondary-container: var(--oscd-base2);\n\n --md-sys-color-surface: var(--oscd-base3);\n --md-sys-color-on-surface: var(--oscd-base00);\n --md-sys-color-surface-variant: var(--oscd-base3);\n --md-sys-color-on-surface-variant: var(--oscd-base00);\n --md-sys-color-surface-bright: var(--oscd-base2);\n --md-sys-color-surface-container: var(--oscd-base3);\n --md-sys-color-surface-container-high: var(--oscd-base3);\n --md-sys-color-surface-container-highest: var(--oscd-base3);\n\n --md-sys-color-outline-variant: var(--oscd-primary);\n --md-sys-color-scrim: #000000;\n --md-sys-color-error: var(--oscd-error);\n --md-sys-color-on-error: var(--oscd-base3);\n\n /* --md-menu-item-selected-label-text-color: var(--oscd-base01); */\n --md-icon-button-disabled-icon-color: var(--oscd-base3);\n\n /* MDC Theme Colors \n * Needed for supporting any pluggins still using the depricated MWC Components\n */\n --mdc-theme-primary: var(--oscd-primary);\n --mdc-theme-secondary: var(--oscd-secondary);\n --mdc-theme-background: var(--oscd-base3);\n --mdc-theme-surface: var(--oscd-base3);\n --mdc-theme-on-primary: var(--oscd-base2);\n --mdc-theme-on-secondary: var(--oscd-base02);\n --mdc-theme-on-background: var(--oscd-base00);\n --mdc-theme-on-surface: var(--oscd-base00);\n --mdc-theme-text-primary-on-background: var(--oscd-base01);\n --mdc-theme-text-secondary-on-background: var(--oscd-base00);\n --mdc-theme-text-icon-on-background: var(--oscd-base00);\n --mdc-theme-error: var(--oscd-error);\n\n --mdc-button-disabled-ink-color: var(--oscd-base1);\n\n --mdc-drawer-heading-ink-color: var(--oscd-base00);\n\n --mdc-dialog-heading-ink-color: var(--oscd-base00);\n\n --mdc-text-field-fill-color: var(--oscd-base2);\n --mdc-text-field-ink-color: var(--oscd-base02);\n --mdc-text-field-label-ink-color: var(--oscd-base01);\n --mdc-text-field-idle-line-color: var(--oscd-base00);\n --mdc-text-field-hover-line-color: var(--oscd-base02);\n\n --mdc-select-fill-color: var(--oscd-base2);\n --mdc-select-ink-color: var(--oscd-base02);\n --mdc-select-label-ink-color: var(--oscd-base01);\n --mdc-select-idle-line-color: var(--oscd-base00);\n --mdc-select-hover-line-color: var(--oscd-base02);\n --mdc-select-dropdown-icon-color: var(--oscd-base01);\n\n --mdc-typography-font-family: var(--oscd-text-font);\n --mdc-icon-font: var(--oscd-icon-font);\n }\n\n oscd-navigation-drawer-header {\n --md-list-item-supporting-text-color: var(--md-sys-color-on-surface);\n }\n oscd-app-bar * {\n --md-filled-icon-button-disabled-container-opacity: 0;\n --md-filled-icon-button-disabled-icon-color: var(\n --md-sys-color-on-primary\n );\n }\n\n oscd-tabs {\n display: flex;\n flex-grow: 1;\n --md-secondary-tab-container-color: var(--oscd-primary);\n }\n oscd-secondary-tab {\n --md-sys-color-on-surface: var(--md-sys-color-on-primary);\n --md-sys-color-on-surface-variant: var(--md-sys-color-on-primary);\n --md-secondary-tab-active-indicator-color: var(--oscd-base2);\n }\n\n .edit-dialog-remove-button {\n --md-text-button-icon-color: var(--oscd-error);\n --md-text-button-label-text-color: var(--oscd-error);\n\n --md-text-button-focus-label-text-color: var(--oscd-error);\n --md-text-button-focus-icon-color: var(--oscd-error);\n\n --md-text-button-hover-label-text-color: var(--oscd-error);\n --md-text-button-hover-state-layer-color: var(--oscd-error);\n --md-text-button-hover-icon-color: var(--oscd-error);\n\n --md-text-button-pressed-label-text-color: var(--oscd-error);\n --md-text-button-pressed-state-layer-color: var(--oscd-error);\n --md-text-button-pressed-icon-color: var(--oscd-error);\n }\n\n #title {\n position: relative;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'oscd-shell': OpenSCD;\n }\n}\n"]}
1
+ {"version":3,"file":"oscd-shell.js","sourceRoot":"","sources":["../oscd-shell.ts"],"names":[],"mappings":"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","sourcesContent":["import '@webcomponents/scoped-custom-element-registry';\n\nimport { configureLocalization, localized, msg } from '@lit/localize';\nimport { css, html, LitElement, nothing, TemplateResult } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\n\nimport '@omicronenergy/oscd-ui/app-bar/oscd-app-bar.js';\nimport type { OscdDialog } from '@omicronenergy/oscd-ui/dialog/OscdDialog.js';\nimport '@omicronenergy/oscd-ui/dialog/oscd-dialog.js';\nimport '@omicronenergy/oscd-ui/divider/oscd-divider.js';\nimport type { OscdFilledIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdFilledIconButton.js';\nimport '@omicronenergy/oscd-ui/iconbutton/oscd-filled-icon-button.js';\nimport '@omicronenergy/oscd-ui/select/oscd-filled-select.js';\nimport '@omicronenergy/oscd-ui/textfield/oscd-filled-text-field.js';\nimport '@omicronenergy/oscd-ui/icon/oscd-icon.js';\nimport type { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';\nimport '@omicronenergy/oscd-ui/list/oscd-list.js';\nimport '@omicronenergy/oscd-ui/list/oscd-list-item.js';\nimport type { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';\nimport '@omicronenergy/oscd-ui/menu/oscd-menu.js';\nimport '@omicronenergy/oscd-ui/menu/oscd-menu-item.js';\nimport type { OscdNavigationDrawer } from '@omicronenergy/oscd-ui/navigation-drawer/OscdNavigationDrawer.js';\nimport '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer.js';\nimport '@omicronenergy/oscd-ui/navigation-drawer/oscd-navigation-drawer-header.js';\nimport '@omicronenergy/oscd-ui/tabs/oscd-secondary-tab.js';\nimport '@omicronenergy/oscd-ui/select/oscd-select-option.js';\nimport '@omicronenergy/oscd-ui/tabs/oscd-tabs.js';\nimport type { OscdTabs } from '@omicronenergy/oscd-ui/tabs/OscdTabs.js';\nimport '@omicronenergy/oscd-ui/button/oscd-text-button.js';\nimport { XMLEditor } from '@omicronenergy/oscd-editor';\nimport {\n EditEventV2,\n EditV2,\n OpenEvent,\n Transactor,\n} from '@omicronenergy/oscd-api';\nimport { allLocales, sourceLocale, targetLocales } from './locales.js';\nimport { loadSourcedPlugins } from './utils/plugin-utils.js';\n\nconst _customElementsDefine = window.customElements.define;\nwindow.customElements.define = (name, cl, conf) => {\n if (!customElements.get(name)) {\n _customElementsDefine.call(window.customElements, name, cl, conf);\n }\n};\n\n/* Copied from API because the export is currently missing, this should be removed when the fixed oscd-api is released */\nexport interface Plugin {\n editor: Transactor<EditV2>;\n docs: Record<string, XMLDocument>;\n doc?: XMLDocument; // the document currently being edited\n docName?: string; // the current doc's name\n stateVersion: unknown; // changes when the document is modified\n /** @deprecated Use `stateVersion` instead */\n editCount: number; // the number of edits made to the document\n locale: string; // the end user's chosen locale\n}\n\nexport type PluginEntry = {\n name: string;\n translations?: Record<(typeof targetLocales)[number], string>;\n tagName: string;\n icon: string;\n requireDoc?: boolean;\n};\n\nexport type SourcedPluginEntry = {\n name: string;\n translations?: Record<(typeof targetLocales)[number], string>;\n src: string;\n icon: string;\n requireDoc?: boolean;\n};\nexport type PluginSet<P = PluginEntry> = {\n menu: P[];\n editor: P[];\n background: P[];\n};\n\ntype Control = {\n icon: string;\n getName: () => string;\n isDisabled: () => boolean;\n action?: () => void | Promise<void>;\n};\n\ntype RenderedPlugin = Control & { tagName: string };\n\ntype LocaleTag = (typeof allLocales)[number];\n\nconst { getLocale, setLocale } = configureLocalization({\n sourceLocale,\n targetLocales,\n loadLocale: locale =>\n import(new URL(`./locales/${locale}.js`, import.meta.url).href),\n});\n\nfunction renderActionItem(\n control: Control,\n slot = 'actionItems',\n): TemplateResult {\n return html`<oscd-filled-icon-button\n slot=\"${slot}\"\n aria-label=\"${control.getName()}\"\n ?disabled=${control.isDisabled()}\n @click=${control.action}\n ><oscd-icon>${control.icon}</oscd-icon></oscd-filled-icon-button\n >`;\n}\n\nfunction renderMenuItem(control: Control): TemplateResult {\n return html`\n <oscd-list-item\n type=\"button\"\n .disabled=${control.isDisabled()}\n @click=${control.action}\n >\n <oscd-icon slot=\"start\">${control.icon}</oscd-icon>\n <span>${control.getName()}</span>\n </oscd-list-item>\n `;\n}\n\n@customElement('oscd-shell')\n@localized()\nexport class OpenSCD extends LitElement {\n /** The file endings of editable files */\n @property({ type: Array, reflect: true }) editable = [\n 'cid',\n 'icd',\n 'iid',\n 'scd',\n 'sed',\n 'ssd',\n ];\n\n isEditable(docName: string): boolean {\n return !!this.editable.find(ext =>\n docName.toLowerCase().endsWith(`.${ext}`),\n );\n }\n\n @state()\n get editableDocs(): string[] {\n return Object.keys(this.docs).filter(name => this.isEditable(name));\n }\n\n private _docs: Record<string, XMLDocument> = {};\n\n /** The set of `XMLDocument`s currently loaded */\n @state()\n get docs(): Record<string, XMLDocument> {\n return this._docs;\n }\n\n set docs(newDocs: Record<string, XMLDocument>) {\n this._docs = newDocs;\n this.onDocsChanged();\n }\n\n onDocsChanged() {\n this.stateVersion += 1;\n }\n\n /** The name of the [[`doc`]] currently being edited */\n @property({ type: String, reflect: true }) docName = '';\n\n @state()\n /** The `XMLDocument` currently being edited */\n get doc(): XMLDocument {\n return this.docs[this.docName];\n }\n\n @state()\n xmlEditor: XMLEditor = new XMLEditor();\n\n @state()\n stateVersion: number = -1;\n\n @state()\n get last(): number {\n return this.xmlEditor.past.length - 1;\n }\n\n @state()\n get canUndo(): boolean {\n return this.xmlEditor.past.length >= 0;\n }\n\n @state()\n get canRedo(): boolean {\n return this.xmlEditor.future.length >= 0;\n }\n\n #plugins: PluginSet = { menu: [], editor: [], background: [] };\n\n @property({ type: Object })\n get plugins(): PluginSet {\n return this.#plugins;\n }\n\n set plugins(\n plugins: Partial<PluginSet<Partial<PluginEntry | SourcedPluginEntry>>>,\n ) {\n this.#plugins = Object.entries(plugins).reduce(\n (acc, [pluginType, kind]) => {\n const convertedPlugins = loadSourcedPlugins(kind);\n acc[pluginType as keyof PluginSet] = convertedPlugins;\n return acc;\n },\n { menu: [], editor: [], background: [] } as PluginSet,\n );\n }\n\n handleOpenDoc({ detail: { docName, doc } }: OpenEvent) {\n this.docs = {\n ...this.docs,\n [docName]: doc,\n };\n if (this.isEditable(docName)) {\n this.docName = docName;\n }\n this.requestUpdate();\n }\n\n /** Undo the last `n` [[Edit]]s committed */\n undo(n = 1) {\n if (!this.canUndo || n < 1) {\n return;\n }\n this.xmlEditor.undo();\n if (n > 1) {\n this.undo(n - 1);\n }\n this.requestUpdate();\n }\n\n /** Redo the last `n` [[Edit]]s that have been undone */\n redo(n = 1) {\n if (!this.canRedo || n < 1) {\n return;\n }\n this.xmlEditor.redo();\n if (n > 1) {\n this.redo(n - 1);\n }\n this.requestUpdate();\n }\n\n @query('#editFile')\n editFileUI!: OscdDialog;\n\n @query('#menu')\n menuUI!: OscdNavigationDrawer;\n\n @query('#fileName')\n fileNameUI!: HTMLInputElement;\n\n @query('#fileExtension')\n fileExtensionUI!: HTMLInputElement;\n\n @query('#fileMenu')\n fileMenuUI!: OscdMenu;\n\n @query('#fileMenuButton')\n fileMenuButtonUI?: OscdFilledIconButton;\n\n @property({ type: String, reflect: true })\n get locale() {\n return getLocale() as LocaleTag;\n }\n\n set locale(tag: LocaleTag) {\n try {\n setLocale(tag);\n } catch {\n // don't change locale if tag is invalid\n }\n }\n\n @state()\n private editorIndex = 0;\n\n @state()\n get editor() {\n return this.editors[this.editorIndex]?.tagName ?? '';\n }\n\n private controls: Record<'undo' | 'redo' | 'menu', Required<Control>> = {\n undo: {\n icon: 'undo',\n getName: () => msg('Undo'),\n action: () => {\n this.undo();\n this.menuUI.opened = false;\n },\n isDisabled: () => !this.canUndo,\n },\n redo: {\n icon: 'redo',\n getName: () => msg('Redo'),\n action: () => {\n this.redo();\n this.menuUI.opened = false;\n },\n isDisabled: () => !this.canRedo,\n },\n\n menu: {\n icon: 'menu',\n getName: () => msg('Menu'),\n action: async () => {\n this.menuUI.opened = !this.menuUI.opened;\n await this.menuUI.updateComplete;\n if (this.menuUI.opened) {\n this.menuUI.querySelector<OscdList>('oscd-list')!.focus();\n }\n },\n isDisabled: () => false,\n },\n };\n\n #actions = [this.controls.undo, this.controls.redo];\n\n @state()\n get menu() {\n return (<Required<Control>[]>this.plugins.menu\n ?.map(\n ({ name, tagName, requireDoc, icon, translations }): RenderedPlugin =>\n ({\n icon,\n getName: () =>\n translations?.[this.locale as (typeof targetLocales)[number]] ||\n name,\n isDisabled: () => (requireDoc && !this.docName) ?? false,\n tagName,\n action: () => {\n if (tagName) {\n this.shadowRoot!.querySelector<\n HTMLElement & { run: () => Promise<void> }\n >(tagName)!.run?.();\n this.menuUI.opened = false;\n }\n },\n }) as RenderedPlugin,\n )\n .filter(p => p !== undefined)).concat(this.#actions);\n }\n\n @state()\n get editors() {\n return <RenderedPlugin[]>this.plugins.editor?.map(\n ({\n name,\n tagName,\n requireDoc,\n icon,\n translations,\n }): RenderedPlugin | undefined =>\n ({\n icon,\n getName: () =>\n translations?.[this.locale as (typeof targetLocales)[number]] ||\n name,\n isDisabled: () => (requireDoc && !this.docName) ?? false,\n tagName,\n }) as RenderedPlugin,\n );\n }\n\n private hotkeys: Partial<Record<string, () => void>> = {\n m: this.controls.menu.action,\n z: this.controls.undo.action,\n y: this.controls.redo.action,\n Z: this.controls.redo.action,\n };\n\n private handleKeyPress(e: KeyboardEvent): void {\n if (!e.ctrlKey) {\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(this.hotkeys, e.key)) {\n return;\n }\n this.hotkeys[e.key]!();\n e.preventDefault();\n }\n\n constructor() {\n super();\n\n document.addEventListener('keydown', event => this.handleKeyPress(event));\n\n this.addEventListener('oscd-open', event => this.handleOpenDoc(event));\n this.addEventListener('oscd-edit-v2', (event: EditEventV2) => {\n this.xmlEditor.commit(event.detail.edit);\n });\n\n // Catch all edits (from commits AND events) and trigger an update\n this.xmlEditor.subscribe(() => {\n this.stateVersion += 1;\n });\n }\n\n updated(changedProps: Map<string, unknown>) {\n super.updated(changedProps);\n\n this.updateComplete.then(() => {\n // Ensure the active tab is set after tabs are rendered\n const oscdTabs = this.shadowRoot!.querySelector('oscd-tabs');\n if (oscdTabs && oscdTabs.activeTabIndex !== this.editorIndex) {\n oscdTabs.activeTabIndex = this.editorIndex;\n }\n });\n }\n\n renderPlugin(tagName: string) {\n const tag = unsafeStatic(tagName);\n return staticHtml`<${tag} \n .locale=\"${this.locale}\"\n .docName=\"${this.docName}\"\n .doc=${this.doc}\n .docs=${this.docs} \n .editCount=${this.stateVersion}\n .stateVersion=${this.stateVersion}\n .editor=${this.xmlEditor}>\n </${tag}>`;\n }\n\n render() {\n return html`<oscd-app-bar slot=\"appContent\">\n ${renderActionItem(this.controls.menu, 'actionStart')}\n <div slot=\"title\" id=\"title\">\n ${this.editableDocs.length > 1\n ? html`<oscd-filled-icon-button\n id=\"fileMenuButton\"\n @click=${() => this.fileMenuUI.show()}\n ><oscd-icon>arrow_drop_down</oscd-icon></oscd-filled-icon-button\n >`\n : nothing}\n ${this.docName}\n ${this.docName\n ? html`<oscd-filled-icon-button\n @click=${() => this.editFileUI.show()}\n ><oscd-icon>edit</oscd-icon></oscd-filled-icon-button\n >`\n : nothing}\n <oscd-menu\n fixed\n id=\"fileMenu\"\n anchor=\"fileMenuButton\"\n corner=\"BOTTOM_END\"\n >\n ${this.editableDocs.map(\n (name, index) =>\n html`<oscd-menu-item\n @click=${() => {\n this.docName = name;\n }}\n ?disabled=${!this.isEditable(name)}\n ?selected=${this.docName === name}\n id=${index}\n >${name}</oscd-menu-item\n >`,\n )}\n </oscd-menu>\n </div>\n\n <div slot=\"actionEnd\">\n ${this.#actions.map(op => renderActionItem(op))}\n </div>\n\n <oscd-tabs\n .activeTabIndex=${this.editorIndex}\n @change=${(event: Event) => {\n const tabs = event.currentTarget as OscdTabs;\n this.editorIndex = tabs.activeTabIndex;\n }}\n >\n ${this.editors.map(editor =>\n editor.isDisabled()\n ? nothing\n : html`<oscd-secondary-tab>\n <oscd-icon slot=\"icon\">${editor.icon}</oscd-icon>\n ${editor.getName()}\n </oscd-secondary-tab>`,\n )}\n </oscd-tabs>\n </oscd-app-bar>\n\n <oscd-navigation-drawer hasheader type=\"modal\" id=\"menu\">\n <oscd-navigation-drawer-header>\n <span slot=\"headline\">${msg('Menu')}</span>\n ${this.docName\n ? html`<span slot=\"supporting-text\">${this.docName}</span>`\n : ''}\n </oscd-navigation-drawer-header>\n <oscd-list>\n <oscd-divider></oscd-divider>\n ${this.menu.map(renderMenuItem)}\n </oscd-list>\n </oscd-navigation-drawer>\n\n ${this.editor ? this.renderPlugin(this.editor) : nothing}\n\n <oscd-dialog\n id=\"editFile\"\n @closed=${(event: CustomEvent) => {\n const dialog = event.target as OscdDialog;\n if (dialog.returnValue === 'remove') {\n delete this.docs[this.docName];\n this.docName = this.editableDocs[0] || '';\n }\n }}\n >\n <div slot=\"headline\">\n <oscd-icon>description</oscd-icon>${this.docName}\n </div>\n <form slot=\"content\" id=\"edit-file-form\" method=\"dialog\">\n <oscd-filled-text-field\n id=\"fileName\"\n label=\"${msg('Filename')}\"\n value=\"${this.docName.replace(/\\.[^.]+$/, '')}\"\n @input=${(event: Event) => {\n const input = event.target as HTMLInputElement;\n const { value } = input;\n const name = `${value}.${this.fileExtensionUI.value}`;\n if (name in this.docs && name !== this.docName) {\n input.setCustomValidity('File already exists!');\n } else {\n input.setCustomValidity('');\n }\n input.reportValidity();\n }}\n ></oscd-filled-text-field>\n <oscd-filled-select\n label=\"${msg('Extension')}\"\n fixedMenuPosition\n id=\"fileExtension\"\n @selected=${() => this.fileNameUI.reportValidity()}\n >\n ${this.editable.map(\n ext =>\n html`<oscd-select-option\n ?selected=${this.docName.endsWith(`.${ext}`)}\n value=\"${ext}\"\n >${ext}</oscd-select-option\n >`,\n )}\n </oscd-filled-select>\n </form>\n <div slot=\"actions\">\n <oscd-text-button\n form=\"edit-file-form\"\n class=\"edit-dialog-remove-button\"\n value=\"remove\"\n >\n <oscd-icon slot=\"icon\">delete</oscd-icon>\n ${msg('Close file')}\n </oscd-text-button>\n <oscd-text-button form=\"edit-file-form\" value=\"close\">\n ${msg('Cancel')}\n </oscd-text-button>\n <oscd-text-button\n @click=${() => {\n const valid = this.fileNameUI.checkValidity();\n if (!valid) {\n this.fileNameUI.reportValidity();\n return;\n }\n const newDocName = `${this.fileNameUI.value}.${this.fileExtensionUI.value}`;\n const doc = this.docs[this.docName];\n delete this.docs[this.docName];\n this.docs = {\n ...this.docs,\n [newDocName]: doc,\n };\n this.docName = newDocName;\n this.editFileUI.close();\n }}\n trailing-icon\n >\n <oscd-icon slot=\"icon\">edit</oscd-icon>\n ${msg('Rename')}\n </oscd-text-button>\n </div>\n </oscd-dialog>\n\n <aside>\n <div class=\"menu-plugins\">\n ${this.plugins.menu.map(plugin => this.renderPlugin(plugin.tagName))}\n </div>\n <div class=\"background-plugins\">\n ${this.plugins.background\n .filter(plugin => !plugin.requireDoc || !!this.docName)\n .map(plugin => this.renderPlugin(plugin.tagName))}\n </div>\n </aside> `;\n }\n\n firstUpdated() {\n const background = getComputedStyle(this.menuUI).getPropertyValue(\n '--oscd-base2',\n );\n document.body.style.background = background;\n }\n\n static styles = css`\n .fileext {\n opacity: 0.81;\n }\n\n .filename {\n caret-color: var(--oscd-secondary);\n }\n\n .filename:focus {\n outline: none;\n }\n\n aside {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n overflow: hidden;\n margin: 0;\n padding: 0;\n }\n\n abbr {\n text-decoration: none;\n }\n\n @media (prefers-color-scheme: light) {\n * {\n --oscd-primary: var(--oscd-theme-primary, #2aa198);\n --oscd-secondary: var(--oscd-theme-secondary, #6c71c4);\n --oscd-base03: var(--oscd-theme-base03, #002b36);\n --oscd-base02: var(--oscd-theme-base02, #073642);\n --oscd-base01: var(--oscd-theme-base01, #586e75);\n --oscd-base00: var(--oscd-theme-base00, #657b83);\n --oscd-base0: var(--oscd-theme-base0, #839496);\n --oscd-base1: var(--oscd-theme-base1, #93a1a1);\n --oscd-base2: var(--oscd-theme-base2, #eee8d5);\n --oscd-base3: var(--oscd-theme-base3, #fdf6e3);\n\n --mdc-theme-text-disabled-on-light: rgba(255, 255, 255, 0.38);\n }\n }\n\n @media (prefers-color-scheme: dark) {\n * {\n --oscd-primary: var(--oscd-theme-secondary, #6c71c4);\n --oscd-secondary: var(--oscd-theme-primary, #2aa198);\n --oscd-base03: var(--oscd-theme-base3, #fdf6e3);\n --oscd-base02: var(--oscd-theme-base2, #eee8d5);\n --oscd-base01: var(--oscd-theme-base1, #93a1a1);\n --oscd-base00: var(--oscd-theme-base0, #839496);\n --oscd-base0: var(--oscd-theme-base00, #657b83);\n --oscd-base1: var(--oscd-theme-base01, #586e75);\n --oscd-base2: var(--oscd-theme-base02, #073642);\n --oscd-base3: var(--oscd-theme-base03, #002b36);\n }\n }\n\n * {\n --oscd-error: var(--oscd-theme-error, #dc322f);\n\n --oscd-text-font: var(--oscd-theme-text-font, 'Roboto');\n --oscd-icon-font: var(--oscd-theme-icon-font, 'Material Icons');\n --oscd-text-font-mono: var(--oscd-theme-text-font-mono, 'Roboto Mono');\n\n --oscd-warning: var(--oscd-theme-warning, #b58900);\n --md-sys-color-primary: var(--oscd-primary);\n --md-sys-color-on-primary: var(--oscd-base3);\n\n --md-sys-color-secondary: var(--oscd-secondary);\n --md-sys-color-on-secondary: var(--oscd-base3);\n --md-sys-color-secondary-container: var(--oscd-base2);\n\n --md-sys-color-surface: var(--oscd-base3);\n --md-sys-color-on-surface: var(--oscd-base00);\n --md-sys-color-surface-variant: var(--oscd-base3);\n --md-sys-color-on-surface-variant: var(--oscd-base00);\n --md-sys-color-surface-bright: var(--oscd-base2);\n --md-sys-color-surface-container: var(--oscd-base3);\n --md-sys-color-surface-container-high: var(--oscd-base3);\n --md-sys-color-surface-container-highest: var(--oscd-base3);\n\n --md-sys-color-outline-variant: var(--oscd-primary);\n --md-sys-color-scrim: #000000;\n --md-sys-color-error: var(--oscd-error);\n --md-sys-color-on-error: var(--oscd-base3);\n\n /* --md-menu-item-selected-label-text-color: var(--oscd-base01); */\n --md-icon-button-disabled-icon-color: var(--oscd-base3);\n\n /* MDC Theme Colors \n * Needed for supporting any pluggins still using the depricated MWC Components\n */\n --mdc-theme-primary: var(--oscd-primary);\n --mdc-theme-secondary: var(--oscd-secondary);\n --mdc-theme-background: var(--oscd-base3);\n --mdc-theme-surface: var(--oscd-base3);\n --mdc-theme-on-primary: var(--oscd-base2);\n --mdc-theme-on-secondary: var(--oscd-base02);\n --mdc-theme-on-background: var(--oscd-base00);\n --mdc-theme-on-surface: var(--oscd-base00);\n --mdc-theme-text-primary-on-background: var(--oscd-base01);\n --mdc-theme-text-secondary-on-background: var(--oscd-base00);\n --mdc-theme-text-icon-on-background: var(--oscd-base00);\n --mdc-theme-error: var(--oscd-error);\n\n --mdc-button-disabled-ink-color: var(--oscd-base1);\n\n --mdc-drawer-heading-ink-color: var(--oscd-base00);\n\n --mdc-dialog-heading-ink-color: var(--oscd-base00);\n\n --mdc-text-field-fill-color: var(--oscd-base2);\n --mdc-text-field-ink-color: var(--oscd-base02);\n --mdc-text-field-label-ink-color: var(--oscd-base01);\n --mdc-text-field-idle-line-color: var(--oscd-base00);\n --mdc-text-field-hover-line-color: var(--oscd-base02);\n\n --mdc-select-fill-color: var(--oscd-base2);\n --mdc-select-ink-color: var(--oscd-base02);\n --mdc-select-label-ink-color: var(--oscd-base01);\n --mdc-select-idle-line-color: var(--oscd-base00);\n --mdc-select-hover-line-color: var(--oscd-base02);\n --mdc-select-dropdown-icon-color: var(--oscd-base01);\n\n --mdc-typography-font-family: var(--oscd-text-font);\n --mdc-icon-font: var(--oscd-icon-font);\n }\n\n oscd-navigation-drawer-header {\n --md-list-item-supporting-text-color: var(--md-sys-color-on-surface);\n }\n oscd-app-bar * {\n --md-filled-icon-button-disabled-container-opacity: 0;\n --md-filled-icon-button-disabled-icon-color: var(\n --md-sys-color-on-primary\n );\n }\n\n oscd-tabs {\n display: flex;\n flex-grow: 1;\n --md-secondary-tab-container-color: var(--oscd-primary);\n }\n oscd-secondary-tab {\n --md-sys-color-on-surface: var(--md-sys-color-on-primary);\n --md-sys-color-on-surface-variant: var(--md-sys-color-on-primary);\n --md-secondary-tab-active-indicator-color: var(--oscd-base2);\n }\n\n .edit-dialog-remove-button {\n --md-text-button-icon-color: var(--oscd-error);\n --md-text-button-label-text-color: var(--oscd-error);\n\n --md-text-button-focus-label-text-color: var(--oscd-error);\n --md-text-button-focus-icon-color: var(--oscd-error);\n\n --md-text-button-hover-label-text-color: var(--oscd-error);\n --md-text-button-hover-state-layer-color: var(--oscd-error);\n --md-text-button-hover-icon-color: var(--oscd-error);\n\n --md-text-button-pressed-label-text-color: var(--oscd-error);\n --md-text-button-pressed-state-layer-color: var(--oscd-error);\n --md-text-button-pressed-icon-color: var(--oscd-error);\n }\n\n #title {\n position: relative;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'oscd-shell': OpenSCD;\n }\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import { PluginEntry, SourcedPluginEntry } from '../oscd-shell.js';
2
+ /**
3
+ * Checks if the given object is a valid Plugin.
4
+ * @param plugin - The object to check.
5
+ * @returns true if the object is a Plugin, false otherwise.
6
+ */
7
+ export declare function isPlugin(plugin: unknown): plugin is PluginEntry;
8
+ /**
9
+ * Checks if the given object is a SourcedPlugin.
10
+ * @param plugin - The object to check.
11
+ * @returns true if the object is a SourcedPlugin, false otherwise.
12
+ */
13
+ export declare function isSourcedPlugin(plugin: unknown): plugin is SourcedPluginEntry;
14
+ /**
15
+ * Validates a Plugin object, checking for required fields and types.
16
+ * If the plugin is invalid, it logs an error and returns undefined.
17
+ * @param plugin - The plugin object to validate.
18
+ * @returns The validated Plugin object or undefined if invalid.
19
+ */
20
+ export declare function validatePlugin(plugin: unknown): PluginEntry | undefined;
21
+ /**
22
+ * Goes through all the plugins in the PluginSet and loads any sourced plugins, replacing the src field with a tagName.
23
+ * If a plugin does not have a tagName, it will be generated based on its src.
24
+ * All plugins returned are validated for required fields.
25
+ * If a sourced plugin fails to load (bad src), it will be replaced with an Error Web Component.
26
+ * @param plugins - Array of plugins to convert.
27
+ * @returns Array of plugins with tagName included.
28
+ */
29
+ export declare function loadSourcedPlugins(plugins: Partial<PluginEntry | SourcedPluginEntry>[]): PluginEntry[];
@@ -0,0 +1,138 @@
1
+ import { cyrb64 } from '../foundation/cyrb64.js';
2
+ const pluginTags = new Map();
3
+ /*
4
+ * Generates a unique tag name for a plugin based on its source URI.
5
+ * This is used to ensure that each plugin has a unique identifier in the CustomElements registry.
6
+ * @param uri - The source URI of the plugin.
7
+ * @returns A unique tag name for the plugin.
8
+ */
9
+ function pluginTag(uri) {
10
+ if (!pluginTags.has(uri)) {
11
+ pluginTags.set(uri, `oscd-p${cyrb64(uri)}`);
12
+ }
13
+ return pluginTags.get(uri);
14
+ }
15
+ /**
16
+ * Generates a Web Component class that displays an error message when a plugin fails to load.
17
+ * This is used to provide feedback to the Distro developer when a plugin cannot be loaded due to an error.
18
+ * @param plugin - The plugin object that failed to load.
19
+ * @returns A Web Component class that displays the error message.
20
+ */
21
+ function generateErrorWcClass(plugin) {
22
+ const title = 'Error: Plugin failed to load.';
23
+ const details = `Plugin: ${JSON.stringify(plugin)}`;
24
+ const classString = `
25
+ return class extends HTMLElement {
26
+
27
+ connectedCallback() {
28
+ this.innerHTML = '<h1>${title}</h1><p>${details}</p><emphasis>Check your plugins.json</emphasis>';
29
+ }
30
+
31
+ async run() {
32
+ alert('${title}\\n\\n ${details}; \\n\\n Check your plugins.json');
33
+ }
34
+ }`;
35
+ // eslint-disable-next-line no-new-func
36
+ return new Function(classString)();
37
+ }
38
+ /**
39
+ * Checks if the given object is a valid Plugin.
40
+ * @param plugin - The object to check.
41
+ * @returns true if the object is a Plugin, false otherwise.
42
+ */
43
+ export function isPlugin(plugin) {
44
+ return (typeof plugin === 'object' &&
45
+ plugin !== null &&
46
+ 'tagName' in plugin &&
47
+ typeof plugin.tagName === 'string');
48
+ }
49
+ /**
50
+ * Checks if the given object is a SourcedPlugin.
51
+ * @param plugin - The object to check.
52
+ * @returns true if the object is a SourcedPlugin, false otherwise.
53
+ */
54
+ export function isSourcedPlugin(plugin) {
55
+ return (typeof plugin === 'object' &&
56
+ plugin !== null &&
57
+ 'src' in plugin &&
58
+ typeof plugin.src === 'string');
59
+ }
60
+ /**
61
+ * Validates a Plugin object, checking for required fields and types.
62
+ * If the plugin is invalid, it logs an error and returns undefined.
63
+ * @param plugin - The plugin object to validate.
64
+ * @returns The validated Plugin object or undefined if invalid.
65
+ */
66
+ export function validatePlugin(plugin) {
67
+ const missingFields = [];
68
+ if (!isPlugin(plugin)) {
69
+ missingFields.push('tagName');
70
+ }
71
+ const _plugin = plugin;
72
+ missingFields.push(...['name', 'icon'].filter(field => !_plugin[field] || typeof _plugin[field] !== 'string'));
73
+ if (typeof _plugin.requireDoc !== 'undefined' &&
74
+ typeof _plugin.requireDoc !== 'boolean') {
75
+ missingFields.push('requireDoc');
76
+ }
77
+ if (typeof _plugin.translations !== 'undefined' &&
78
+ (typeof _plugin.translations !== 'object' ||
79
+ Object.values(_plugin.translations).some(t => typeof t !== 'string'))) {
80
+ missingFields.push('translations');
81
+ }
82
+ if (missingFields.length > 0) {
83
+ // eslint-disable-next-line no-console
84
+ console.error(`[Invalid Plugin]\n${JSON.stringify(plugin, null, 2)}\nMissing/Invalid fields [${missingFields.join(',')}] - skipping.`);
85
+ return undefined;
86
+ }
87
+ return _plugin;
88
+ }
89
+ /**
90
+ * Goes through all the plugins in the PluginSet and loads any sourced plugins, replacing the src field with a tagName.
91
+ * If a plugin does not have a tagName, it will be generated based on its src.
92
+ * All plugins returned are validated for required fields.
93
+ * If a sourced plugin fails to load (bad src), it will be replaced with an Error Web Component.
94
+ * @param plugins - Array of plugins to convert.
95
+ * @returns Array of plugins with tagName included.
96
+ */
97
+ export function loadSourcedPlugins(plugins) {
98
+ return plugins
99
+ .map(plugin => {
100
+ if (isPlugin(plugin)) {
101
+ return validatePlugin(plugin);
102
+ }
103
+ if (!isSourcedPlugin(plugin)) {
104
+ // eslint-disable-next-line no-console
105
+ console.error(`[Invalid Plugin] Requires a tagName or src - skipping. ${JSON.stringify(plugin)}`);
106
+ return undefined;
107
+ }
108
+ const { src, ...rest } = plugin;
109
+ const hashedTagName = pluginTag(src);
110
+ const validatedPlugin = validatePlugin({
111
+ ...rest,
112
+ tagName: hashedTagName,
113
+ });
114
+ if (!validatedPlugin) {
115
+ return undefined;
116
+ }
117
+ if (customElements.get(hashedTagName)) {
118
+ return validatedPlugin;
119
+ }
120
+ const url = new URL(src, window.location.href).toString();
121
+ import(url)
122
+ .then(mod => {
123
+ // Because this is async, we need to check (again) if the element is already defined.
124
+ if (!customElements?.get(hashedTagName)) {
125
+ customElements.define(hashedTagName, mod.default);
126
+ }
127
+ })
128
+ .catch(err => {
129
+ // eslint-disable-next-line no-console
130
+ console.error(`[Invalid Plugin] Failed to load plugin ${plugin.name} from ${url}`, err);
131
+ const ErrWc = generateErrorWcClass(plugin);
132
+ customElements.define(hashedTagName, ErrWc);
133
+ });
134
+ return validatedPlugin;
135
+ })
136
+ .filter((plugin) => plugin !== undefined);
137
+ }
138
+ //# sourceMappingURL=plugin-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-utils.js","sourceRoot":"","sources":["../../utils/plugin-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { cyrb64 } from '../foundation/cyrb64.js';\nimport { PluginEntry, SourcedPluginEntry } from '../oscd-shell.js';\n\nconst pluginTags = new Map<string, string>();\n\n/*\n * Generates a unique tag name for a plugin based on its source URI.\n * This is used to ensure that each plugin has a unique identifier in the CustomElements registry.\n * @param uri - The source URI of the plugin.\n * @returns A unique tag name for the plugin.\n */\nfunction pluginTag(uri: string): string {\n if (!pluginTags.has(uri)) {\n pluginTags.set(uri, `oscd-p${cyrb64(uri)}`);\n }\n return pluginTags.get(uri)!;\n}\n\n/**\n * Generates a Web Component class that displays an error message when a plugin fails to load.\n * This is used to provide feedback to the Distro developer when a plugin cannot be loaded due to an error.\n * @param plugin - The plugin object that failed to load.\n * @returns A Web Component class that displays the error message.\n */\nfunction generateErrorWcClass(plugin: Partial<PluginEntry>) {\n const title = 'Error: Plugin failed to load.';\n const details = `Plugin: ${JSON.stringify(plugin)}`;\n const classString = `\n return class extends HTMLElement {\n\n connectedCallback() {\n this.innerHTML = '<h1>${title}</h1><p>${details}</p><emphasis>Check your plugins.json</emphasis>';\n }\n\n async run() {\n alert('${title}\\\\n\\\\n ${details}; \\\\n\\\\n Check your plugins.json');\n }\n }`;\n // eslint-disable-next-line no-new-func\n return new Function(classString)();\n}\n\n/**\n * Checks if the given object is a valid Plugin.\n * @param plugin - The object to check.\n * @returns true if the object is a Plugin, false otherwise.\n */\nexport function isPlugin(plugin: unknown): plugin is PluginEntry {\n return (\n typeof plugin === 'object' &&\n plugin !== null &&\n 'tagName' in plugin &&\n typeof plugin.tagName === 'string'\n );\n}\n\n/**\n * Checks if the given object is a SourcedPlugin.\n * @param plugin - The object to check.\n * @returns true if the object is a SourcedPlugin, false otherwise.\n */\nexport function isSourcedPlugin(plugin: unknown): plugin is SourcedPluginEntry {\n return (\n typeof plugin === 'object' &&\n plugin !== null &&\n 'src' in plugin &&\n typeof plugin.src === 'string'\n );\n}\n\n/**\n * Validates a Plugin object, checking for required fields and types.\n * If the plugin is invalid, it logs an error and returns undefined.\n * @param plugin - The plugin object to validate.\n * @returns The validated Plugin object or undefined if invalid.\n */\nexport function validatePlugin(plugin: unknown): PluginEntry | undefined {\n const missingFields = [];\n if (!isPlugin(plugin)) {\n missingFields.push('tagName');\n }\n\n const _plugin = plugin as PluginEntry;\n missingFields.push(\n ...(['name', 'icon'] as const).filter(\n field => !_plugin[field] || typeof _plugin[field] !== 'string',\n ),\n );\n if (\n typeof _plugin.requireDoc !== 'undefined' &&\n typeof _plugin.requireDoc !== 'boolean'\n ) {\n missingFields.push('requireDoc');\n }\n if (\n typeof _plugin.translations !== 'undefined' &&\n (typeof _plugin.translations !== 'object' ||\n Object.values(_plugin.translations).some(t => typeof t !== 'string'))\n ) {\n missingFields.push('translations');\n }\n\n if (missingFields.length > 0) {\n // eslint-disable-next-line no-console\n console.error(\n `[Invalid Plugin]\\n${JSON.stringify(plugin, null, 2)}\\nMissing/Invalid fields [${missingFields.join(',')}] - skipping.`,\n );\n return undefined;\n }\n return _plugin;\n}\n\n/**\n * Goes through all the plugins in the PluginSet and loads any sourced plugins, replacing the src field with a tagName.\n * If a plugin does not have a tagName, it will be generated based on its src.\n * All plugins returned are validated for required fields.\n * If a sourced plugin fails to load (bad src), it will be replaced with an Error Web Component.\n * @param plugins - Array of plugins to convert.\n * @returns Array of plugins with tagName included.\n */\nexport function loadSourcedPlugins(\n plugins: Partial<PluginEntry | SourcedPluginEntry>[],\n): PluginEntry[] {\n return plugins\n .map(plugin => {\n if (isPlugin(plugin)) {\n return validatePlugin(plugin);\n }\n if (!isSourcedPlugin(plugin)) {\n // eslint-disable-next-line no-console\n console.error(\n `[Invalid Plugin] Requires a tagName or src - skipping. ${JSON.stringify(plugin)}`,\n );\n return undefined;\n }\n const { src, ...rest } = plugin as SourcedPluginEntry;\n const hashedTagName = pluginTag(src);\n const validatedPlugin = validatePlugin({\n ...rest,\n tagName: hashedTagName,\n });\n\n if (!validatedPlugin) {\n return undefined;\n }\n\n if (customElements.get(hashedTagName)) {\n return validatedPlugin;\n }\n\n const url = new URL(src, window.location.href).toString();\n import(url)\n .then(mod => {\n // Because this is async, we need to check (again) if the element is already defined.\n if (!customElements?.get(hashedTagName)) {\n customElements.define(hashedTagName, mod.default);\n }\n })\n .catch(err => {\n // eslint-disable-next-line no-console\n console.error(\n `[Invalid Plugin] Failed to load plugin ${plugin.name} from ${url}`,\n err,\n );\n const ErrWc = generateErrorWcClass(plugin);\n customElements.define(hashedTagName, ErrWc);\n });\n return validatedPlugin;\n })\n .filter((plugin): plugin is PluginEntry => plugin !== undefined);\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { isPlugin, isSourcedPlugin, validatePlugin } from './plugin-utils.js';
3
+ describe('Plugin Utils', () => {
4
+ describe('isPlugin', () => {
5
+ it('should return true for a valid Plugin object', () => {
6
+ const plugin = {
7
+ tagName: 'test-plugin',
8
+ name: 'Test Plugin',
9
+ icon: 'test-icon',
10
+ requireDoc: false,
11
+ };
12
+ expect(plugin).satisfies(isPlugin);
13
+ });
14
+ it('should return false for an object without tagName', () => {
15
+ const plugin = {
16
+ name: 'Test Plugin',
17
+ icon: 'test-icon',
18
+ requireDoc: false,
19
+ };
20
+ expect(plugin).to.not.satisfy(isPlugin);
21
+ });
22
+ it('should return false for a SourcePlugin', () => {
23
+ const plugin = {
24
+ name: 'Test Plugin',
25
+ icon: 'test-icon',
26
+ src: 'data:text/javascript;charset=utf-8,import%20%7B%20default%20as%20TestPlugin%20%7D%20from%20"./test-plugin.js";',
27
+ };
28
+ expect(plugin).to.not.satisfy(isPlugin);
29
+ });
30
+ });
31
+ describe('isSourcePlugin', () => {
32
+ it('should return true for a valid SourcePlugin object', () => {
33
+ const plugin = {
34
+ name: 'Test Plugin',
35
+ src: 'data:text/javascript;charset=utf-8,import%20%7B%20default%20as%20TestPlugin%20%7D%20from%20"./test-plugin.js";',
36
+ icon: 'test-icon',
37
+ };
38
+ expect(plugin).satisfies(isSourcedPlugin);
39
+ });
40
+ it('should return false for an object without a src field', () => {
41
+ const plugin = {
42
+ name: 'Test Plugin',
43
+ icon: 'test-icon',
44
+ };
45
+ expect(plugin).not.to.satisfy(isSourcedPlugin);
46
+ });
47
+ it('should return false for an Plugin object (has tagName, but no src)', () => {
48
+ const plugin = {
49
+ name: 'Test Plugin',
50
+ icon: 'test-icon',
51
+ tagName: 'test-plugin',
52
+ };
53
+ expect(plugin).not.to.satisfy(isSourcedPlugin);
54
+ });
55
+ });
56
+ });
57
+ describe('validatePlugin', () => {
58
+ it('returns a Plugin object, for a valid plugin', async () => {
59
+ const plugin = {
60
+ name: 'Tagless, Sourceless, Hopeless Plugin',
61
+ icon: 'coronavirus',
62
+ tagName: 'test-tagless-plugin',
63
+ };
64
+ expect(plugin).satisfies(validatePlugin);
65
+ });
66
+ it('returns undefined, for a plugin definition missing its "tagName" field', async () => {
67
+ const plugin = {
68
+ icon: 'coronavirus',
69
+ name: 'Tagless, Sourceless, Hopeless Plugin',
70
+ };
71
+ expect(plugin).not.to.satisfy(validatePlugin);
72
+ });
73
+ it('returns undefined, for a plugin definition missing its "name" field', async () => {
74
+ const plugin = {
75
+ icon: 'coronavirus',
76
+ tagName: 'test-tagless-plugin',
77
+ };
78
+ expect(plugin).not.to.satisfy(validatePlugin);
79
+ });
80
+ it('returns undefined, for a plugin definition missing its "icon" field', async () => {
81
+ const plugin = {
82
+ name: 'Tagless, Sourceless, Hopeless Plugin',
83
+ tagName: 'test-tagless-plugin',
84
+ };
85
+ expect(plugin).not.to.satisfy(validatePlugin);
86
+ });
87
+ it('returns undefined, for a plugin definition with invalid requireDoc field', async () => {
88
+ const plugin = {
89
+ name: 'Tagless, Sourceless, Hopeless Plugin',
90
+ tagName: 'test-tagless-plugin',
91
+ requireDoc: 'not-a-boolean',
92
+ };
93
+ expect(plugin).not.to.satisfy(validatePlugin);
94
+ });
95
+ it('returns undefined, for a plugin definition with invalid translations field', async () => {
96
+ const plugin = {
97
+ name: 'Tagless, Sourceless, Hopeless Plugin',
98
+ tagName: 'test-tagless-plugin',
99
+ translations: 'ops',
100
+ };
101
+ expect(plugin).not.to.satisfy(validatePlugin);
102
+ });
103
+ it('returns undefined, for a plugin definition with invalid translations', async () => {
104
+ const plugin = {
105
+ name: 'Tagless, Sourceless, Hopeless Plugin',
106
+ tagName: 'test-tagless-plugin',
107
+ translations: {
108
+ en: 'Tagless, Sourceless, Hopeless Plugin',
109
+ fr: 123, // Invalid translation (not a string)
110
+ },
111
+ };
112
+ expect(plugin).not.to.satisfy(validatePlugin);
113
+ });
114
+ });
115
+ //# sourceMappingURL=plugin-utils.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-utils.spec.js","sourceRoot":"","sources":["../../utils/plugin-utils.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9E,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG;gBACb,OAAO,EAAE,aAAa;gBACtB,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,KAAK;aAClB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,KAAK;aAClB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,WAAW;gBACjB,GAAG,EAAE,gHAAgH;aACtH,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,aAAa;gBACnB,GAAG,EAAE,gHAAgH;gBACrH,IAAI,EAAE,WAAW;aAClB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,WAAW;aAClB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;YAC5E,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,aAAa;aACvB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,sCAAsC;YAC5C,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,qBAAqB;SAC/B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,sCAAsC;SAC7C,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,qBAAqB;SAC/B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,sCAAsC;YAC5C,OAAO,EAAE,qBAAqB;SAC/B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,sCAAsC;YAC5C,OAAO,EAAE,qBAAqB;YAC9B,UAAU,EAAE,eAAe;SAC5B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,sCAAsC;YAC5C,OAAO,EAAE,qBAAqB;YAC9B,YAAY,EAAE,KAAK;SACpB,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,sCAAsC;YAC5C,OAAO,EAAE,qBAAqB;YAC9B,YAAY,EAAE;gBACZ,EAAE,EAAE,sCAAsC;gBAC1C,EAAE,EAAE,GAAG,EAAE,qCAAqC;aAC/C;SACF,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { isPlugin, isSourcedPlugin, validatePlugin } from './plugin-utils.js';\n\ndescribe('Plugin Utils', () => {\n describe('isPlugin', () => {\n it('should return true for a valid Plugin object', () => {\n const plugin = {\n tagName: 'test-plugin',\n name: 'Test Plugin',\n icon: 'test-icon',\n requireDoc: false,\n };\n expect(plugin).satisfies(isPlugin);\n });\n\n it('should return false for an object without tagName', () => {\n const plugin = {\n name: 'Test Plugin',\n icon: 'test-icon',\n requireDoc: false,\n };\n expect(plugin).to.not.satisfy(isPlugin);\n });\n\n it('should return false for a SourcePlugin', () => {\n const plugin = {\n name: 'Test Plugin',\n icon: 'test-icon',\n src: 'data:text/javascript;charset=utf-8,import%20%7B%20default%20as%20TestPlugin%20%7D%20from%20\"./test-plugin.js\";',\n };\n expect(plugin).to.not.satisfy(isPlugin);\n });\n });\n\n describe('isSourcePlugin', () => {\n it('should return true for a valid SourcePlugin object', () => {\n const plugin = {\n name: 'Test Plugin',\n src: 'data:text/javascript;charset=utf-8,import%20%7B%20default%20as%20TestPlugin%20%7D%20from%20\"./test-plugin.js\";',\n icon: 'test-icon',\n };\n expect(plugin).satisfies(isSourcedPlugin);\n });\n\n it('should return false for an object without a src field', () => {\n const plugin = {\n name: 'Test Plugin',\n icon: 'test-icon',\n };\n expect(plugin).not.to.satisfy(isSourcedPlugin);\n });\n\n it('should return false for an Plugin object (has tagName, but no src)', () => {\n const plugin = {\n name: 'Test Plugin',\n icon: 'test-icon',\n tagName: 'test-plugin',\n };\n expect(plugin).not.to.satisfy(isSourcedPlugin);\n });\n });\n});\n\ndescribe('validatePlugin', () => {\n it('returns a Plugin object, for a valid plugin', async () => {\n const plugin = {\n name: 'Tagless, Sourceless, Hopeless Plugin',\n icon: 'coronavirus',\n tagName: 'test-tagless-plugin',\n };\n\n expect(plugin).satisfies(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition missing its \"tagName\" field', async () => {\n const plugin = {\n icon: 'coronavirus',\n name: 'Tagless, Sourceless, Hopeless Plugin',\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition missing its \"name\" field', async () => {\n const plugin = {\n icon: 'coronavirus',\n tagName: 'test-tagless-plugin',\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition missing its \"icon\" field', async () => {\n const plugin = {\n name: 'Tagless, Sourceless, Hopeless Plugin',\n tagName: 'test-tagless-plugin',\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition with invalid requireDoc field', async () => {\n const plugin = {\n name: 'Tagless, Sourceless, Hopeless Plugin',\n tagName: 'test-tagless-plugin',\n requireDoc: 'not-a-boolean',\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition with invalid translations field', async () => {\n const plugin = {\n name: 'Tagless, Sourceless, Hopeless Plugin',\n tagName: 'test-tagless-plugin',\n translations: 'ops',\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n\n it('returns undefined, for a plugin definition with invalid translations', async () => {\n const plugin = {\n name: 'Tagless, Sourceless, Hopeless Plugin',\n tagName: 'test-tagless-plugin',\n translations: {\n en: 'Tagless, Sourceless, Hopeless Plugin',\n fr: 123, // Invalid translation (not a string)\n },\n };\n\n expect(plugin).not.to.satisfy(validatePlugin);\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omicronenergy/oscd-shell",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "The shell component of OpenSCD, where plugins are loaded and the application is initialized.",
5
5
  "displayName": "OpenSCD Shell",
6
6
  "repository": {
@@ -22,10 +22,7 @@
22
22
  "url": "https://github.com/OMICRONEnergyOSS/oscd-shell/issues"
23
23
  },
24
24
  "homepage": "https://github.com/OMICRONEnergyOSS/oscd-shell#readme",
25
- "packageManager": "npm@11.3.0",
26
25
  "type": "module",
27
- "browser": "./dist/foundation.js",
28
- "main": "./dist/foundation.js",
29
26
  "exports": {
30
27
  ".": "./dist/foundation.js",
31
28
  "./oscd-shell.js": "./dist/oscd-shell.js"
@@ -38,6 +35,10 @@
38
35
  "scripts": {
39
36
  "lint": "eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore",
40
37
  "format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore",
38
+ "build": "rimraf dist && npm run extract && npm run localize && tsc -b",
39
+ "bundle": "rimraf dist && npm run localize && rollup -c rollup.config.js",
40
+ "extract": "lit-localize extract",
41
+ "localize": "lit-localize build",
41
42
  "start": "npm run build && cp -r ./demo ./dist/ && concurrently -k -r \"tsc -b --watch --preserveWatchOutput\" \"wds\"",
42
43
  "start:bundle": "npm run bundle && concurrently -k -r \"rollup -c rollup.config.js --watch\" \"wds --open dist/demo/index.html --watch\"",
43
44
  "test": "npm run build && playwright install && wtr --coverage",
@@ -47,18 +48,17 @@
47
48
  "test:unit:watch": "npm run build && concurrently -k -r \"tsc -b --watch --preserveWatchOutput\" \"wtr --watch --coverage --group default\"",
48
49
  "test:update": "npm run build && wtr --group visual --update-visual-baseline",
49
50
  "analyze": "cem analyze --litelement",
50
- "extract": "lit-localize extract",
51
- "localize": "lit-localize build",
52
- "build": "rimraf dist && npm run extract && npm run localize && tsc -b",
53
51
  "doc": "npm run analyze -- --exclude dist && typedoc --out dist/doc oscd-shell.ts foundation.ts",
54
- "bundle": "rimraf dist && npm run localize && rollup -c rollup.config.js",
55
52
  "deploy": "npm run bundle && npm run doc && gh-pages --dist 'dist'",
56
53
  "prepare": "husky"
57
54
  },
58
55
  "dependencies": {
59
56
  "@lit/localize": "^0.12.2",
60
57
  "@omicronenergy/oscd-api": "^0.1.0",
58
+ "@omicronenergy/oscd-background-editv1": "^0.0.6",
61
59
  "@omicronenergy/oscd-editor": "^1.5.0",
60
+ "@omicronenergy/oscd-menu-open": "^0.0.6",
61
+ "@omicronenergy/oscd-menu-save": "^0.0.1",
62
62
  "@omicronenergy/oscd-test-utils": "^0.0.7",
63
63
  "@omicronenergy/oscd-ui": "^0.0.4",
64
64
  "@open-wc/scoped-elements": "^3.0.5",
@@ -99,7 +99,7 @@
99
99
  "sinon": "^21.0.0",
100
100
  "tsdoc": "^0.0.4",
101
101
  "typedoc": "^0.28.4",
102
- "typescript": "^5.5.3"
102
+ "typescript": "5.5.3"
103
103
  },
104
104
  "overrides": {
105
105
  "playwright": {
@@ -123,7 +123,7 @@
123
123
  "plugins": [
124
124
  "@typescript-eslint",
125
125
  "eslint-plugin-tsdoc",
126
- "prettier/recommended"
126
+ "prettier"
127
127
  ],
128
128
  "rules": {
129
129
  "no-unused-vars": "off",
@@ -146,7 +146,8 @@
146
146
  {
147
147
  "devDependencies": [
148
148
  "**/*.test.ts",
149
- "**/*.spec.ts"
149
+ "**/*.spec.ts",
150
+ "./dist"
150
151
  ]
151
152
  }
152
153
  ],
@@ -169,7 +170,8 @@
169
170
  {
170
171
  "ignorePackages": true
171
172
  }
172
- ]
173
+ ],
174
+ "prettier/prettier": "error"
173
175
  },
174
176
  "overrides": [
175
177
  {