@jxsuite/studio 0.23.2 → 0.24.0

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.
Files changed (105) hide show
  1. package/dist/studio.js +2230 -714
  2. package/dist/studio.js.map +121 -104
  3. package/package.json +8 -7
  4. package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
  5. package/src/browse/{browse.js → browse.ts} +109 -84
  6. package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
  7. package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
  8. package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
  9. package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
  10. package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
  11. package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
  12. package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
  13. package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
  14. package/src/editor/{context-menu.js → context-menu.ts} +38 -36
  15. package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
  16. package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
  17. package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
  18. package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
  19. package/src/editor/{inline-format.js → inline-format.ts} +46 -51
  20. package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
  21. package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
  22. package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
  23. package/src/files/{components.js → components.ts} +12 -15
  24. package/src/files/{file-ops.js → file-ops.ts} +36 -29
  25. package/src/files/{files.js → files.ts} +102 -84
  26. package/src/github/{github-auth.js → github-auth.ts} +3 -3
  27. package/src/github/{github-publish.js → github-publish.ts} +20 -18
  28. package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
  29. package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
  30. package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
  31. package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
  32. package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
  33. package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
  34. package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
  35. package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
  36. package/src/panels/dnd.ts +417 -0
  37. package/src/panels/{editors.js → editors.ts} +43 -50
  38. package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
  39. package/src/panels/{events-panel.js → events-panel.ts} +33 -25
  40. package/src/panels/{git-panel.js → git-panel.ts} +97 -83
  41. package/src/panels/{head-panel.js → head-panel.ts} +117 -139
  42. package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
  43. package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
  44. package/src/panels/{left-panel.js → left-panel.ts} +56 -63
  45. package/src/panels/{overlays.js → overlays.ts} +28 -36
  46. package/src/panels/{panel-events.js → panel-events.ts} +53 -51
  47. package/src/panels/{preview-render.js → preview-render.ts} +20 -15
  48. package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
  49. package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
  50. package/src/panels/{quick-search.js → quick-search.ts} +16 -17
  51. package/src/panels/{right-panel.js → right-panel.ts} +41 -59
  52. package/src/panels/{shared.js → shared.ts} +7 -5
  53. package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
  54. package/src/panels/{statusbar.js → statusbar.ts} +14 -14
  55. package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
  56. package/src/panels/{style-panel.js → style-panel.ts} +235 -290
  57. package/src/panels/{style-utils.js → style-utils.ts} +32 -31
  58. package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
  59. package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
  60. package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
  61. package/src/panels/{toolbar.js → toolbar.ts} +68 -70
  62. package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
  63. package/src/{platform.js → platform.ts} +5 -3
  64. package/src/platforms/{devserver.js → devserver.ts} +53 -44
  65. package/src/{recent-projects.js → recent-projects.ts} +17 -12
  66. package/src/{resize-edges.js → resize-edges.ts} +18 -13
  67. package/src/services/{cem-export.js → cem-export.ts} +14 -7
  68. package/src/services/{code-services.js → code-services.ts} +29 -20
  69. package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
  70. package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
  71. package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
  72. package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
  73. package/src/settings/{general-settings.js → general-settings.ts} +29 -28
  74. package/src/settings/{head-editor.js → head-editor.ts} +37 -35
  75. package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
  76. package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
  77. package/src/{site-context.js → site-context.ts} +35 -32
  78. package/src/{state.js → state.ts} +95 -78
  79. package/src/{store.js → store.ts} +60 -63
  80. package/src/{studio.js → studio.ts} +117 -119
  81. package/src/tabs/tab.ts +196 -0
  82. package/src/tabs/{transact.js → transact.ts} +107 -60
  83. package/src/types.ts +185 -0
  84. package/src/ui/{button-group.js → button-group.ts} +21 -21
  85. package/src/ui/{color-selector.js → color-selector.ts} +44 -37
  86. package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
  87. package/src/ui/{field-row.js → field-row.ts} +19 -2
  88. package/src/ui/{icons.js → icons.ts} +2 -2
  89. package/src/ui/{layers.js → layers.ts} +33 -24
  90. package/src/ui/{media-picker.js → media-picker.ts} +13 -9
  91. package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
  92. package/src/ui/{spectrum.js → spectrum.ts} +4 -4
  93. package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
  94. package/src/ui/{value-selector.js → value-selector.ts} +28 -28
  95. package/src/ui/{widgets.js → widgets.ts} +62 -15
  96. package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
  97. package/src/utils/{edit-display.js → edit-display.ts} +23 -20
  98. package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
  99. package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
  100. package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
  101. package/src/{view.js → view.ts} +53 -55
  102. package/src/workspace/{workspace.js → workspace.ts} +44 -33
  103. package/src/panels/dnd.js +0 -426
  104. package/src/tabs/tab.js +0 -192
  105. /package/src/{reactivity.js → reactivity.ts} +0 -0
@@ -1,12 +1,15 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Pseudo-state preview — extracted from studio.js (Phase 4m). When a pseudo-selector (:hover,
3
4
  * :focus, etc.) is active in the style sidebar, force those styles onto the selected element.
4
5
  */
5
6
 
6
- import { getNodeAtPath } from "../store.js";
7
- import { activeTab } from "../workspace/workspace.js";
8
- import { view } from "../view.js";
9
- import { getActivePanel, findCanvasElement } from "../canvas/canvas-helpers.js";
7
+ import { getNodeAtPath } from "../store";
8
+ import { activeTab } from "../workspace/workspace";
9
+ import { view } from "../view";
10
+ import { getActivePanel, findCanvasElement } from "../canvas/canvas-helpers";
11
+
12
+ import type { JxStyle } from "@jxsuite/schema/types";
10
13
 
11
14
  const pseudoStyleHost = document.createElement("div");
12
15
  pseudoStyleHost.style.display = "contents";
@@ -34,16 +37,14 @@ export function updateForcedPseudoPreview() {
34
37
  const node = getNodeAtPath(tab.doc.document, tab.session.selection);
35
38
  if (!node?.style) return;
36
39
  const activeMedia = tab.session.ui.activeMedia;
37
- /** @type {JxStyle} */
38
- const ctx = activeMedia ? node.style[`@${activeMedia}`] || {} : node.style;
40
+ const ctx: JxStyle = activeMedia ? node.style[`@${activeMedia}`] || {} : node.style;
39
41
  const rules = ctx[sel];
40
42
  if (!rules || typeof rules !== "object") return;
41
43
 
42
44
  const cssProps = Object.entries(rules)
43
45
  .filter(([k]) => typeof rules[k] === "string" || typeof rules[k] === "number")
44
46
  .map(
45
- ([k, v]) =>
46
- `${k.replace(/[A-Z]/g, (/** @type {string} */ c) => `-${c.toLowerCase()}`)}: ${v} !important`,
47
+ ([k, v]) => `${k.replace(/[A-Z]/g, (c: string) => `-${c.toLowerCase()}`)}: ${v} !important`,
47
48
  )
48
49
  .join("; ");
49
50
  if (!cssProps) return;
@@ -1,16 +1,17 @@
1
+ /// <reference lib="dom" />
1
2
  import { html, render as litRender, nothing } from "lit-html";
2
3
  import { classMap } from "lit-html/directives/class-map.js";
3
4
  import { live } from "lit-html/directives/live.js";
4
5
  import { ref } from "lit-html/directives/ref.js";
5
- import { getPlatform } from "../platform.js";
6
- import { openFileInTab } from "../files/files.js";
7
- import { getRecentFiles, trackRecentFile } from "../recent-projects.js";
8
- import { getLayerSlot } from "../ui/layers.js";
6
+ import { getPlatform } from "../platform";
7
+ import { openFileInTab } from "../files/files";
8
+ import { getRecentFiles, trackRecentFile } from "../recent-projects";
9
+ import { getLayerSlot } from "../ui/layers";
9
10
 
10
11
  let _open = false;
11
12
  let _query = "";
12
13
  /** @type {{ path: string; name?: string }[]} */
13
- let _results = [];
14
+ let _results: { path: string; name?: string }[] = [];
14
15
  let _selectedIndex = 0;
15
16
  let _debounceTimer = 0;
16
17
 
@@ -36,7 +37,7 @@ export function closeQuickSearch() {
36
37
  renderOverlay();
37
38
  }
38
39
 
39
- async function doSearch(/** @type {string} */ query) {
40
+ async function doSearch(query: string) {
40
41
  if (!query.trim()) {
41
42
  _results = [];
42
43
  _selectedIndex = 0;
@@ -54,16 +55,14 @@ async function doSearch(/** @type {string} */ query) {
54
55
  }
55
56
  }
56
57
 
57
- function onInput(/** @type {Event} */ e) {
58
- _query = /** @type {HTMLInputElement} */ (e.target).value;
58
+ function onInput(e: Event) {
59
+ _query = (e.target as HTMLInputElement).value;
59
60
  clearTimeout(_debounceTimer);
60
- _debounceTimer = /** @type {number} */ (
61
- /** @type {unknown} */ (setTimeout(() => doSearch(_query), 150))
62
- );
61
+ _debounceTimer = setTimeout(() => doSearch(_query), 150) as unknown as number;
63
62
  renderOverlay();
64
63
  }
65
64
 
66
- function onKeydown(/** @type {KeyboardEvent} */ e) {
65
+ function onKeydown(e: KeyboardEvent) {
67
66
  const items = _query.trim() ? _results : getRecentFiles();
68
67
  switch (e.key) {
69
68
  case "ArrowDown":
@@ -87,14 +86,14 @@ function onKeydown(/** @type {KeyboardEvent} */ e) {
87
86
  }
88
87
  }
89
88
 
90
- function selectItem(/** @type {{ path: string; name?: string }} */ item) {
89
+ function selectItem(item: { path: string; name?: string }) {
91
90
  closeQuickSearch();
92
91
  const path = item.path;
93
92
  trackRecentFile({ path, name: path.split("/").pop() || "" });
94
93
  openFileInTab(path);
95
94
  }
96
95
 
97
- function fileIcon(/** @type {string} */ name) {
96
+ function fileIcon(name: string) {
98
97
  const ext = name.split(".").pop()?.toLowerCase();
99
98
  switch (ext) {
100
99
  case "json":
@@ -106,7 +105,7 @@ function fileIcon(/** @type {string} */ name) {
106
105
  }
107
106
  }
108
107
 
109
- function dirPart(/** @type {string} */ path) {
108
+ function dirPart(path: string) {
110
109
  const parts = path.split("/");
111
110
  parts.pop();
112
111
  return parts.length ? parts.join("/") : "";
@@ -125,7 +124,7 @@ function renderOverlay() {
125
124
 
126
125
  const tpl = html`
127
126
  <div class="quick-search-overlay" @click=${closeQuickSearch}>
128
- <div class="quick-search-panel" @click=${(/** @type {Event} */ e) => e.stopPropagation()}>
127
+ <div class="quick-search-panel" @click=${(e: Event) => e.stopPropagation()}>
129
128
  <input
130
129
  class="quick-search-input"
131
130
  type="text"
@@ -134,7 +133,7 @@ function renderOverlay() {
134
133
  @input=${onInput}
135
134
  @keydown=${onKeydown}
136
135
  ${ref((el) => {
137
- if (el) requestAnimationFrame(() => /** @type {HTMLInputElement} */ (el).focus());
136
+ if (el) requestAnimationFrame(() => (el as HTMLInputElement).focus());
138
137
  })}
139
138
  />
140
139
  <div class="quick-search-results">
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Right panel — orchestrates Properties / Events / Style tabs. The heavy sub-templates
3
4
  * (propertiesSidebarTemplate, renderStylePanelTemplate) remain in studio.js and are passed via ctx
@@ -5,43 +6,39 @@
5
6
  */
6
7
 
7
8
  import { html, render as litRender } from "lit-html";
8
- import { updateUi, rightPanel } from "../store.js";
9
- import { effect, effectScope } from "../reactivity.js";
10
- import { activeTab } from "../workspace/workspace.js";
11
- import { tabIcon } from "./activity-bar.js";
12
- import { eventsSidebarTemplate } from "./events-panel.js";
13
- import { isCustomElementDoc } from "./signals-panel.js";
9
+ import { updateUi, rightPanel } from "../store";
10
+ import { effect, effectScope } from "../reactivity";
11
+ import { activeTab } from "../workspace/workspace";
12
+ import { tabIcon } from "./activity-bar";
13
+ import { eventsSidebarTemplate } from "./events-panel";
14
+ import { isCustomElementDoc } from "./signals-panel";
15
+
16
+ import { isColorPopoverOpen } from "../ui/color-selector";
17
+ import { renderStylePanelTemplate } from "./style-panel";
18
+ import { renderPropertiesPanelTemplate } from "./properties-panel";
19
+ interface RightPanelCtx {
20
+ navigateToComponent: (path: string) => void;
21
+ getCanvasMode: () => string;
22
+ renderCanvas: () => void;
23
+ updateForcedPseudoPreview: () => void;
24
+ }
14
25
 
15
- import { isColorPopoverOpen } from "../ui/color-selector.js";
16
- import { renderStylePanelTemplate } from "./style-panel.js";
17
- import { renderPropertiesPanelTemplate } from "./properties-panel.js";
18
26
  import {
19
27
  renderAiPanelTemplate,
20
28
  mountAiPanel,
21
29
  mountQuikChat,
22
30
  registerRightPanelRender,
23
- } from "./ai-panel.js";
31
+ } from "./ai-panel";
24
32
 
25
- /**
26
- * @typedef {{
27
- * navigateToComponent: (path: string) => void;
28
- * getCanvasMode: () => string;
29
- * renderCanvas: () => void;
30
- * updateForcedPseudoPreview: () => void;
31
- * }} RightPanelCtx
32
- */
33
+ let _ctx: RightPanelCtx | null = null;
33
34
 
34
- /** @type {RightPanelCtx | null} */
35
- let _ctx = null;
36
-
37
- /** @type {import("@vue/reactivity").EffectScope | null} */
38
- let _scope = null;
35
+ let _scope: import("@vue/reactivity").EffectScope | null = null;
39
36
 
40
37
  let _rendering = false;
41
38
  let _scheduled = false;
42
39
  let _hasFocus = false;
43
40
 
44
- function _isTextInput(/** @type {Element | null} */ el) {
41
+ function _isTextInput(el: Element | null) {
45
42
  if (!el) return false;
46
43
  const tag = el.tagName.toLowerCase();
47
44
  if (tag === "input" || tag === "textarea") return true;
@@ -50,8 +47,8 @@ function _isTextInput(/** @type {Element | null} */ el) {
50
47
  return false;
51
48
  }
52
49
 
53
- function _onFocusIn(/** @type {FocusEvent} */ e) {
54
- _hasFocus = _isTextInput(/** @type {Element} */ (e.target));
50
+ function _onFocusIn(e: FocusEvent) {
51
+ _hasFocus = _isTextInput(e.target as Element);
55
52
  }
56
53
 
57
54
  function _onFocusOut() {
@@ -64,7 +61,7 @@ function _onFocusOut() {
64
61
  *
65
62
  * @param {RightPanelCtx} ctx
66
63
  */
67
- export function mount(ctx) {
64
+ export function mount(ctx: RightPanelCtx) {
68
65
  _ctx = ctx;
69
66
  mountAiPanel();
70
67
  registerRightPanelRender(render);
@@ -119,16 +116,11 @@ export function render() {
119
116
  }
120
117
  }
121
118
 
122
- /** @type {HTMLElement | null} */
123
- let _propsContainer = null;
124
- /** @type {HTMLElement | null} */
125
- let _eventsContainer = null;
126
- /** @type {HTMLElement | null} */
127
- let _styleContainer = null;
128
- /** @type {HTMLElement | null} */
129
- let _assistantContainer = null;
130
- /** @type {string | null} */
131
- let _lastTab = null;
119
+ let _propsContainer: HTMLElement | null = null;
120
+ let _eventsContainer: HTMLElement | null = null;
121
+ let _styleContainer: HTMLElement | null = null;
122
+ let _assistantContainer: HTMLElement | null = null;
123
+ let _lastTab: string | null = null;
132
124
 
133
125
  function _ensureContainers() {
134
126
  if (_propsContainer) return;
@@ -151,26 +143,19 @@ function _flush() {
151
143
  if (_hasFocus || isColorPopoverOpen()) return;
152
144
  _rendering = true;
153
145
  try {
154
- const ctx = /** @type {RightPanelCtx} */ (_ctx);
146
+ const ctx = _ctx as RightPanelCtx;
155
147
  const aTab = activeTab.value;
156
148
  if (!aTab) {
157
149
  rightPanel.textContent = "";
158
150
  return;
159
151
  }
160
- const S = /**
161
- * @type {{
162
- * ui: Record<string, unknown>;
163
- * document: JxMutableNode;
164
- * mode: string;
165
- * selection: (string | number)[] | null;
166
- * }}
167
- */ ({
152
+ const S = {
168
153
  ui: aTab.session.ui,
169
154
  document: aTab.doc.document,
170
155
  mode: aTab.doc.mode,
171
156
  selection: aTab.session.selection,
172
- });
173
- const tab = /** @type {string} */ (S.ui.rightTab);
157
+ };
158
+ const tab = S.ui.rightTab;
174
159
 
175
160
  // Render tabs header
176
161
  const panelTabs = [
@@ -184,7 +169,7 @@ function _flush() {
184
169
  <sp-tabs
185
170
  selected=${tab}
186
171
  quiet
187
- @change=${(/** @type {Event & { target: { selected: string } }} */ e) => {
172
+ @change=${(e: Event & { target: { selected: string } }) => {
188
173
  const sel = e.target.selected;
189
174
  if (sel && sel !== tab) {
190
175
  updateUi("rightTab", sel);
@@ -204,12 +189,12 @@ function _flush() {
204
189
  `;
205
190
 
206
191
  _ensureContainers();
207
- const containers = /** @type {HTMLElement[]} */ ([
192
+ const containers = [
208
193
  _propsContainer,
209
194
  _eventsContainer,
210
195
  _styleContainer,
211
196
  _assistantContainer,
212
- ]);
197
+ ] as HTMLElement[];
213
198
  const tabKeys = ["properties", "events", "style", "assistant"];
214
199
 
215
200
  // Show/hide containers
@@ -231,24 +216,21 @@ function _flush() {
231
216
  if (tab === "properties") {
232
217
  litRender(
233
218
  renderPropertiesPanelTemplate({ navigateToComponent: ctx.navigateToComponent }),
234
- /** @type {HTMLElement} */ (_propsContainer),
219
+ _propsContainer!,
235
220
  );
236
221
  } else if (tab === "events") {
237
222
  litRender(
238
223
  eventsSidebarTemplate({ isCustomElementDoc: () => isCustomElementDoc(S) }),
239
- /** @type {HTMLElement} */ (_eventsContainer),
224
+ _eventsContainer!,
240
225
  );
241
226
  } else if (tab === "style") {
242
227
  try {
243
- litRender(
244
- renderStylePanelTemplate({ getCanvasMode: ctx.getCanvasMode }),
245
- /** @type {HTMLElement} */ (_styleContainer),
246
- );
247
- } catch (/** @type {unknown} */ e) {
228
+ litRender(renderStylePanelTemplate({ getCanvasMode: ctx.getCanvasMode }), _styleContainer!);
229
+ } catch (e) {
248
230
  console.error("[renderStylePanelTemplate]", e);
249
231
  }
250
232
  } else if (tab === "assistant") {
251
- litRender(renderAiPanelTemplate(), /** @type {HTMLElement} */ (_assistantContainer));
233
+ litRender(renderAiPanelTemplate(), _assistantContainer!);
252
234
  }
253
235
 
254
236
  _lastTab = tab;
@@ -1,20 +1,23 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Shared panel utilities — portable helpers extracted from studio.js. These functions depend only
3
4
  * on store.js / state.js exports (no circular deps).
4
5
  */
5
6
 
7
+ import type { JxMutableNode } from "@jxsuite/schema/types";
8
+
6
9
  /**
7
10
  * Convert a $media key like "--tablet" to a friendly display name "Tablet". "--" returns "Base".
8
11
  *
9
12
  * @param {string} name
10
13
  */
11
- export function mediaDisplayName(name) {
14
+ export function mediaDisplayName(name: string) {
12
15
  if (name === "--") return "Base";
13
16
  return (
14
17
  name
15
18
  .replace(/^--/, "")
16
19
  .replace(/-/g, " ")
17
- .replace(/\b\w/g, (/** @type {string} */ c) => c.toUpperCase()) || name
20
+ .replace(/\b\w/g, (c: string) => c.toUpperCase()) || name
18
21
  );
19
22
  }
20
23
 
@@ -25,9 +28,8 @@ export const unsafeTags = new Set(["script", "style", "link", "iframe", "object"
25
28
  *
26
29
  * @param {string} tag
27
30
  */
28
- export function defaultDef(tag) {
29
- /** @type {JxMutableNode} */
30
- const def = { tagName: tag };
31
+ export function defaultDef(tag: string) {
32
+ const def: JxMutableNode = { tagName: tag };
31
33
  if (/^h[1-6]$/.test(tag)) def.textContent = "Heading";
32
34
  else if (tag === "p") def.textContent = "Paragraph text";
33
35
  else if (