@shelamkoff/rector 1.0.0 → 1.0.2

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 (94) hide show
  1. package/README.md +25 -23
  2. package/dist/core/clipboard/Clipboard.d.ts +2 -0
  3. package/dist/core/clipboard/Clipboard.js +52 -24
  4. package/dist/core/index.js +195 -194
  5. package/dist/core/locale/en.d.ts +1 -0
  6. package/dist/core/locale/en.js +1 -0
  7. package/dist/core/locale/ru.d.ts +1 -0
  8. package/dist/core/locale/ru.js +1 -0
  9. package/dist/core/themes/light.css +1 -1
  10. package/dist/core/themes/variables.css +72 -10
  11. package/dist/inline-plugins/mention/index.js +1 -2
  12. package/dist/locale/en.d.ts +41 -0
  13. package/dist/locale/ru.d.ts +41 -0
  14. package/dist/plugins/attaches/attaches.css +16 -14
  15. package/dist/plugins/attaches/index.js +997 -921
  16. package/dist/plugins/attaches/locale/en.d.ts +9 -0
  17. package/dist/plugins/attaches/locale/en.js +11 -2
  18. package/dist/plugins/attaches/locale/ru.d.ts +9 -0
  19. package/dist/plugins/attaches/locale/ru.js +11 -2
  20. package/dist/plugins/carousel/carousel.css +213 -49
  21. package/dist/plugins/carousel/index.js +918 -859
  22. package/dist/plugins/carousel/locale/en.d.ts +15 -0
  23. package/dist/plugins/carousel/locale/en.js +18 -3
  24. package/dist/plugins/carousel/locale/ru.d.ts +15 -0
  25. package/dist/plugins/carousel/locale/ru.js +18 -3
  26. package/dist/plugins/checklist/index.js +1 -2
  27. package/dist/plugins/code/index.js +1 -2
  28. package/dist/plugins/columns/index.js +1 -2
  29. package/dist/plugins/delimiter/index.js +1 -2
  30. package/dist/plugins/embed/index.js +1 -2
  31. package/dist/plugins/gallery/gallery.css +6 -1
  32. package/dist/plugins/gallery/index.js +375 -355
  33. package/dist/plugins/gallery/locale/en.d.ts +8 -0
  34. package/dist/plugins/gallery/locale/en.js +11 -3
  35. package/dist/plugins/gallery/locale/ru.d.ts +8 -0
  36. package/dist/plugins/gallery/locale/ru.js +11 -3
  37. package/dist/plugins/gallery/view-empty.d.ts +2 -0
  38. package/dist/plugins/gallery/view-empty.js +21 -15
  39. package/dist/plugins/gallery/view-filled.d.ts +2 -2
  40. package/dist/plugins/gallery/view-filled.js +61 -56
  41. package/dist/plugins/heading/index.js +1 -2
  42. package/dist/plugins/image/image.css +7 -2
  43. package/dist/plugins/image/index.js +365 -346
  44. package/dist/plugins/image/locale/en.d.ts +8 -0
  45. package/dist/plugins/image/locale/en.js +11 -3
  46. package/dist/plugins/image/locale/ru.d.ts +8 -0
  47. package/dist/plugins/image/locale/ru.js +11 -3
  48. package/dist/plugins/image/view-empty.d.ts +2 -0
  49. package/dist/plugins/image/view-empty.js +21 -15
  50. package/dist/plugins/image/view-filled.d.ts +2 -2
  51. package/dist/plugins/image/view-filled.js +77 -72
  52. package/dist/plugins/link-preview/index.js +1 -2
  53. package/dist/plugins/list/index.js +1 -2
  54. package/dist/plugins/paragraph/index.js +1 -2
  55. package/dist/plugins/person/index.js +1 -2
  56. package/dist/plugins/poll/index.js +1 -2
  57. package/dist/plugins/quote/index.js +1 -2
  58. package/dist/plugins/raw/index.js +1 -2
  59. package/dist/plugins/shared/dropzone.d.ts +6 -0
  60. package/dist/plugins/shared/dropzone.js +47 -36
  61. package/dist/plugins/shared/layer.d.ts +13 -0
  62. package/dist/plugins/shared/layer.js +45 -0
  63. package/dist/plugins/shared/sourceEditor.css +165 -0
  64. package/dist/plugins/shared/sourceEditor.d.ts +97 -0
  65. package/dist/plugins/shared/sourceEditor.js +260 -0
  66. package/dist/plugins/spoiler/index.js +1 -2
  67. package/dist/plugins/table/index.js +1 -2
  68. package/dist/plugins/toggle/index.js +1 -2
  69. package/dist/plugins/warning/index.js +1 -2
  70. package/dist/renderer/index.js +1 -2
  71. package/dist/renderer/renderers/attaches/index.js +1 -2
  72. package/dist/renderer/renderers/carousel/index.js +24 -24
  73. package/dist/renderer/renderers/carousel/styles.css +8 -2
  74. package/dist/renderer/renderers/checklist/index.js +1 -2
  75. package/dist/renderer/renderers/code/index.js +1 -2
  76. package/dist/renderer/renderers/columns/index.js +1 -2
  77. package/dist/renderer/renderers/delimiter/index.js +1 -2
  78. package/dist/renderer/renderers/embed/index.js +1 -2
  79. package/dist/renderer/renderers/gallery/index.js +1 -2
  80. package/dist/renderer/renderers/heading/index.js +1 -2
  81. package/dist/renderer/renderers/image/index.js +1 -2
  82. package/dist/renderer/renderers/link-preview/index.js +1 -2
  83. package/dist/renderer/renderers/list/index.js +1 -2
  84. package/dist/renderer/renderers/paragraph/index.js +1 -2
  85. package/dist/renderer/renderers/person/index.js +1 -2
  86. package/dist/renderer/renderers/poll/index.js +1 -2
  87. package/dist/renderer/renderers/quote/index.js +1 -2
  88. package/dist/renderer/renderers/spoiler/index.js +1 -2
  89. package/dist/renderer/renderers/table/index.js +1 -2
  90. package/dist/renderer/renderers/toggle/index.js +1 -2
  91. package/dist/renderer/renderers/warning/index.js +1 -2
  92. package/package.json +27 -29
  93. package/dist/shared/resolvePath.d.ts +0 -8
  94. package/dist/shared/resolvePath.js +0 -10
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Keep a plugin surface above neighbouring editor blocks while it is open.
3
+ * Calls are reference-counted so a source editor can coexist with a settings
4
+ * panel owned by the same block.
5
+ *
6
+ * @param {HTMLElement} owner Element inside the plugin block.
7
+ * @param {AbortSignal} signal Plugin render lifecycle signal.
8
+ * @returns {{ open: () => void, close: () => void }}
9
+ */
10
+ export function createPluginLayer(owner: HTMLElement, signal: AbortSignal): {
11
+ open: () => void;
12
+ close: () => void;
13
+ };
@@ -0,0 +1,45 @@
1
+ const openLayers = new WeakMap()
2
+
3
+ /**
4
+ * Keep a plugin surface above neighbouring editor blocks while it is open.
5
+ * Calls are reference-counted so a source editor can coexist with a settings
6
+ * panel owned by the same block.
7
+ *
8
+ * @param {HTMLElement} owner Element inside the plugin block.
9
+ * @param {AbortSignal} signal Plugin render lifecycle signal.
10
+ * @returns {{ open: () => void, close: () => void }}
11
+ */
12
+ export function createPluginLayer(owner, signal) {
13
+ /** @type {HTMLElement | null} */
14
+ let block = null
15
+ let opened = false
16
+
17
+ const close = () => {
18
+ if (!opened || !block) return
19
+ const currentBlock = block
20
+ opened = false
21
+ block = null
22
+ const next = Math.max(0, (openLayers.get(currentBlock) || 1) - 1)
23
+ if (next === 0) {
24
+ openLayers.delete(currentBlock)
25
+ currentBlock.removeAttribute('data-oe-layer-open')
26
+ return
27
+ }
28
+ openLayers.set(currentBlock, next)
29
+ }
30
+
31
+ signal.addEventListener('abort', close, { once: true })
32
+
33
+ return {
34
+ open() {
35
+ if (opened || signal.aborted) return
36
+ const candidate = owner.closest('.oe-block')
37
+ if (!(candidate instanceof HTMLElement)) return
38
+ block = candidate
39
+ opened = true
40
+ openLayers.set(block, (openLayers.get(block) || 0) + 1)
41
+ block.setAttribute('data-oe-layer-open', 'true')
42
+ },
43
+ close,
44
+ }
45
+ }
@@ -0,0 +1,165 @@
1
+ .oe-source-editor {
2
+ position: absolute;
3
+ inset: 0;
4
+ z-index: var(--oe-plugin-dialog-z-index, 1200);
5
+ box-sizing: border-box;
6
+ display: grid;
7
+ place-items: center;
8
+ min-height: 220px;
9
+ padding: 16px;
10
+ }
11
+
12
+ .oe-source-editor--preloaded {
13
+ visibility: hidden;
14
+ opacity: 0;
15
+ pointer-events: none;
16
+ }
17
+
18
+ .oe-source-editor[data-oe-source-editor='html'] {
19
+ min-height: min(26rem, calc(100vh - 2rem));
20
+ }
21
+
22
+ .oe-source-editor__backdrop {
23
+ position: absolute;
24
+ inset: 0;
25
+ width: 100%;
26
+ height: 100%;
27
+ padding: 0;
28
+ background: var(--oe-overlay-dark, rgba(0, 0, 0, 0.7));
29
+ border: 0;
30
+ border-radius: inherit;
31
+ cursor: default;
32
+ }
33
+
34
+ .oe-source-editor__panel {
35
+ position: relative;
36
+ display: grid;
37
+ gap: 12px;
38
+ box-sizing: border-box;
39
+ width: min(100%, 32rem);
40
+ max-height: calc(100vh - 2rem);
41
+ padding: 16px;
42
+ overflow-y: auto;
43
+ background: var(--oe-surface-elevated, #1e1e24);
44
+ border: 1px solid var(--oe-border-hover, #2a2a2a);
45
+ border-radius: var(--oe-radius, 8px);
46
+ box-shadow: var(--oe-toolbar-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
47
+ text-align: left;
48
+ }
49
+
50
+ .oe-source-editor[data-oe-source-editor='url'] .oe-source-editor__panel {
51
+ width: min(100%, 28rem);
52
+ }
53
+
54
+ .oe-source-editor__title {
55
+ margin: 0;
56
+ color: var(--oe-text-1, #f5f5f5);
57
+ font: inherit;
58
+ font-size: 15px;
59
+ font-weight: 650;
60
+ }
61
+
62
+ .oe-source-editor__label {
63
+ display: grid;
64
+ gap: 8px;
65
+ color: var(--oe-text-2, #c4c4c4);
66
+ font-size: 13px;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .oe-source-editor__field {
71
+ box-sizing: border-box;
72
+ width: 100%;
73
+ min-height: 42px;
74
+ padding: 10px 12px;
75
+ resize: vertical;
76
+ color: var(--oe-text-1, #f5f5f5);
77
+ background: var(--oe-input-bg, #2c2c33);
78
+ border: 1px solid var(--oe-border-hover, #2a2a2a);
79
+ border-radius: var(--oe-radius-sm, 4px);
80
+ outline: none;
81
+ font: inherit;
82
+ font-size: 14px;
83
+ line-height: 1.5;
84
+ }
85
+
86
+ .oe-source-editor textarea.oe-source-editor__field {
87
+ min-height: 9rem;
88
+ max-height: min(40vh, 20rem);
89
+ }
90
+
91
+ .oe-source-editor__field:hover {
92
+ background: var(--oe-input-bg-hover, #353540);
93
+ }
94
+
95
+ .oe-source-editor .oe-source-editor__field:focus,
96
+ .oe-source-editor .oe-source-editor__field:focus-visible {
97
+ border-color: var(--oe-border-hover, #2a2a2a);
98
+ outline: none;
99
+ outline-offset: 0;
100
+ box-shadow: none;
101
+ }
102
+
103
+ .oe-source-editor__field[aria-invalid='true'] {
104
+ border-color: var(--oe-danger, #f87171);
105
+ }
106
+
107
+ .oe-source-editor__error {
108
+ color: var(--oe-danger-light, #fca5a5);
109
+ font-size: 13px;
110
+ }
111
+
112
+ .oe-source-editor__actions {
113
+ display: flex;
114
+ justify-content: flex-end;
115
+ gap: 8px;
116
+ }
117
+
118
+ .oe-source-editor__button {
119
+ min-height: 36px;
120
+ padding: 7px 14px;
121
+ border: 1px solid transparent;
122
+ border-radius: var(--oe-radius-sm, 4px);
123
+ font: inherit;
124
+ font-size: 13px;
125
+ font-weight: 650;
126
+ cursor: pointer;
127
+ }
128
+
129
+ .oe-source-editor__button--secondary {
130
+ color: var(--oe-text-1, #f5f5f5);
131
+ background: var(--oe-card, #111);
132
+ border-color: var(--oe-border-hover, #2a2a2a);
133
+ }
134
+
135
+ .oe-source-editor__button--secondary:hover {
136
+ background: var(--oe-card-hover, #191919);
137
+ }
138
+
139
+ .oe-source-editor__button--primary {
140
+ color: var(--oe-text-on-accent, #fff);
141
+ background: var(--oe-accent, #4357b4);
142
+ }
143
+
144
+ .oe-source-editor__button--primary:hover {
145
+ filter: brightness(1.08);
146
+ }
147
+
148
+ @media (max-width: 520px) {
149
+ .oe-source-editor {
150
+ padding: 10px;
151
+ }
152
+
153
+ .oe-source-editor__panel {
154
+ max-height: calc(100vh - 1.25rem);
155
+ padding: 14px;
156
+ }
157
+
158
+ .oe-source-editor__actions {
159
+ flex-direction: column-reverse;
160
+ }
161
+
162
+ .oe-source-editor__button {
163
+ width: 100%;
164
+ }
165
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @typedef {Object} SourceEditorConfig
3
+ * @property {HTMLElement} wrapper Plugin wrapper that owns the editor.
4
+ * @property {AbortSignal} signal Plugin render lifecycle signal.
5
+ * @property {'url' | 'html'} kind Source value kind.
6
+ * @property {string} title Dialog title.
7
+ * @property {string} label Accessible field label.
8
+ * @property {string} placeholder Field placeholder.
9
+ * @property {string} submitText Submit button label.
10
+ * @property {string} cancelText Cancel button label.
11
+ * @property {string} invalidText Validation error.
12
+ * @property {(value: string) => string} normalize Sanitizes and validates input.
13
+ * @property {(value: string) => void} onSubmit Receives the normalized value.
14
+ */
15
+ /**
16
+ * Build URL/HTML forms while the owning plugin view is rendered so their
17
+ * first style and layout pass does not happen in the user's click handler.
18
+ *
19
+ * @param {HTMLElement} wrapper Plugin wrapper that owns the editor.
20
+ * @param {AbortSignal} signal Plugin render lifecycle signal.
21
+ * @param {Array<'url' | 'html'>} kinds Forms used by the current plugin view.
22
+ * @returns {void}
23
+ */
24
+ export function preloadSourceEditor(wrapper: HTMLElement, signal: AbortSignal, kinds: Array<"url" | "html">): void;
25
+ /**
26
+ * Open an editor-owned URL or HTML input surface without relying on native
27
+ * browser prompts.
28
+ *
29
+ * @param {SourceEditorConfig} config Source editor configuration.
30
+ * @returns {{ close: () => void }}
31
+ */
32
+ export function openSourceEditor(config: SourceEditorConfig): {
33
+ close: () => void;
34
+ };
35
+ export type SourceEditorConfig = {
36
+ /**
37
+ * Plugin wrapper that owns the editor.
38
+ */
39
+ wrapper: HTMLElement;
40
+ /**
41
+ * Plugin render lifecycle signal.
42
+ */
43
+ signal: AbortSignal;
44
+ /**
45
+ * Source value kind.
46
+ */
47
+ kind: "url" | "html";
48
+ /**
49
+ * Dialog title.
50
+ */
51
+ title: string;
52
+ /**
53
+ * Accessible field label.
54
+ */
55
+ label: string;
56
+ /**
57
+ * Field placeholder.
58
+ */
59
+ placeholder: string;
60
+ /**
61
+ * Submit button label.
62
+ */
63
+ submitText: string;
64
+ /**
65
+ * Cancel button label.
66
+ */
67
+ cancelText: string;
68
+ /**
69
+ * Validation error.
70
+ */
71
+ invalidText: string;
72
+ /**
73
+ * Sanitizes and validates input.
74
+ */
75
+ normalize: (value: string) => string;
76
+ /**
77
+ * Receives the normalized value.
78
+ */
79
+ onSubmit: (value: string) => void;
80
+ };
81
+ export type SourceEditorSurface = {
82
+ root: HTMLElement;
83
+ backdrop: HTMLButtonElement;
84
+ panel: HTMLFormElement;
85
+ title: HTMLHeadingElement;
86
+ labelText: HTMLSpanElement;
87
+ field: HTMLInputElement | HTMLTextAreaElement;
88
+ error: HTMLDivElement;
89
+ cancel: HTMLButtonElement;
90
+ submit: HTMLButtonElement;
91
+ signal: AbortSignal;
92
+ layer: {
93
+ open: () => void;
94
+ close: () => void;
95
+ };
96
+ destroy: () => void;
97
+ };
@@ -0,0 +1,260 @@
1
+ import { createPluginLayer } from './layer.js'
2
+
3
+ /**
4
+ * @typedef {Object} SourceEditorSurface
5
+ * @property {HTMLElement} root
6
+ * @property {HTMLButtonElement} backdrop
7
+ * @property {HTMLFormElement} panel
8
+ * @property {HTMLHeadingElement} title
9
+ * @property {HTMLSpanElement} labelText
10
+ * @property {HTMLInputElement | HTMLTextAreaElement} field
11
+ * @property {HTMLDivElement} error
12
+ * @property {HTMLButtonElement} cancel
13
+ * @property {HTMLButtonElement} submit
14
+ * @property {AbortSignal} signal
15
+ * @property {{ open: () => void, close: () => void }} layer
16
+ * @property {() => void} destroy
17
+ */
18
+
19
+ /** @type {WeakMap<HTMLElement, { close: () => void, surface: SourceEditorSurface }>} */
20
+ const activeEditors = new WeakMap()
21
+
22
+ /** @type {WeakMap<HTMLElement, Map<'url' | 'html', SourceEditorSurface>>} */
23
+ const surfaces = new WeakMap()
24
+
25
+ /**
26
+ * @typedef {Object} SourceEditorConfig
27
+ * @property {HTMLElement} wrapper Plugin wrapper that owns the editor.
28
+ * @property {AbortSignal} signal Plugin render lifecycle signal.
29
+ * @property {'url' | 'html'} kind Source value kind.
30
+ * @property {string} title Dialog title.
31
+ * @property {string} label Accessible field label.
32
+ * @property {string} placeholder Field placeholder.
33
+ * @property {string} submitText Submit button label.
34
+ * @property {string} cancelText Cancel button label.
35
+ * @property {string} invalidText Validation error.
36
+ * @property {(value: string) => string} normalize Sanitizes and validates input.
37
+ * @property {(value: string) => void} onSubmit Receives the normalized value.
38
+ */
39
+
40
+ /**
41
+ * Build URL/HTML forms while the owning plugin view is rendered so their
42
+ * first style and layout pass does not happen in the user's click handler.
43
+ *
44
+ * @param {HTMLElement} wrapper Plugin wrapper that owns the editor.
45
+ * @param {AbortSignal} signal Plugin render lifecycle signal.
46
+ * @param {Array<'url' | 'html'>} kinds Forms used by the current plugin view.
47
+ * @returns {void}
48
+ */
49
+ export function preloadSourceEditor(wrapper, signal, kinds) {
50
+ if (signal.aborted) return
51
+ for (const kind of new Set(kinds)) ensureSurface(wrapper, signal, kind)
52
+ }
53
+
54
+ /**
55
+ * Open an editor-owned URL or HTML input surface without relying on native
56
+ * browser prompts.
57
+ *
58
+ * @param {SourceEditorConfig} config Source editor configuration.
59
+ * @returns {{ close: () => void }}
60
+ */
61
+ export function openSourceEditor(config) {
62
+ if (config.signal.aborted) return { close() {} }
63
+ activeEditors.get(config.wrapper)?.close()
64
+
65
+ const surface = ensureSurface(config.wrapper, config.signal, config.kind)
66
+ if (!surface) return { close() {} }
67
+ const controller = new AbortController()
68
+ const { root, backdrop, panel, title, labelText, field, error, cancel, submit } = surface
69
+ backdrop.setAttribute('aria-label', config.cancelText)
70
+ panel.setAttribute('aria-label', config.title)
71
+ title.textContent = config.title
72
+ labelText.textContent = config.label
73
+ field.value = ''
74
+ field.placeholder = config.placeholder
75
+ field.removeAttribute('aria-invalid')
76
+ error.textContent = ''
77
+ error.hidden = true
78
+ cancel.textContent = config.cancelText
79
+ submit.textContent = config.submitText
80
+ setSurfaceVisible(surface, true)
81
+ surface.layer.open()
82
+
83
+ let closed = false
84
+ const close = () => {
85
+ if (closed) return
86
+ closed = true
87
+ if (activeEditors.get(config.wrapper)?.close === close) activeEditors.delete(config.wrapper)
88
+ controller.abort()
89
+ surface.layer.close()
90
+ setSurfaceVisible(surface, false)
91
+ }
92
+ activeEditors.set(config.wrapper, { close, surface })
93
+
94
+ config.signal.addEventListener('abort', close, { once: true, signal: controller.signal })
95
+ backdrop.addEventListener('click', close, { signal: controller.signal })
96
+ cancel.addEventListener('click', close, { signal: controller.signal })
97
+ root.addEventListener('keydown', (event) => {
98
+ if (event.key === 'Escape') {
99
+ event.preventDefault()
100
+ event.stopPropagation()
101
+ close()
102
+ }
103
+ }, { signal: controller.signal })
104
+ panel.addEventListener('submit', (event) => {
105
+ event.preventDefault()
106
+ let value = ''
107
+ try {
108
+ value = config.normalize(field.value)
109
+ } catch {
110
+ value = ''
111
+ }
112
+ if (!value) {
113
+ error.textContent = config.invalidText
114
+ error.hidden = false
115
+ field.setAttribute('aria-invalid', 'true')
116
+ field.focus()
117
+ return
118
+ }
119
+ close()
120
+ config.onSubmit(value)
121
+ }, { signal: controller.signal })
122
+ field.addEventListener('input', () => {
123
+ error.hidden = true
124
+ field.removeAttribute('aria-invalid')
125
+ }, { signal: controller.signal })
126
+
127
+ queueMicrotask(() => {
128
+ if (!closed) field.focus({ preventScroll: true })
129
+ })
130
+
131
+ return { close }
132
+ }
133
+
134
+ /**
135
+ * @param {HTMLElement} wrapper
136
+ * @param {AbortSignal} signal
137
+ * @param {'url' | 'html'} kind
138
+ * @returns {SourceEditorSurface | null}
139
+ */
140
+ function ensureSurface(wrapper, signal, kind) {
141
+ if (signal.aborted) return null
142
+
143
+ let owned = surfaces.get(wrapper)
144
+ if (!owned) {
145
+ owned = new Map()
146
+ surfaces.set(wrapper, owned)
147
+ }
148
+
149
+ const cached = owned.get(kind)
150
+ if (cached?.signal === signal && cached.root.isConnected) return cached
151
+ cached?.destroy()
152
+
153
+ const root = document.createElement('div')
154
+ root.className = 'oe-source-editor oe-source-editor--preloaded'
155
+ root.dataset.oeSourceEditor = kind
156
+ root.setAttribute('aria-hidden', 'true')
157
+ root.inert = true
158
+
159
+ const backdrop = document.createElement('button')
160
+ backdrop.type = 'button'
161
+ backdrop.className = 'oe-source-editor__backdrop'
162
+ backdrop.setAttribute('aria-label', 'Cancel')
163
+ backdrop.tabIndex = -1
164
+
165
+ const panel = document.createElement('form')
166
+ panel.className = 'oe-source-editor__panel'
167
+ panel.noValidate = true
168
+ panel.setAttribute('role', 'dialog')
169
+ panel.setAttribute('aria-modal', 'true')
170
+ panel.setAttribute('aria-label', 'Source')
171
+
172
+ const title = document.createElement('h3')
173
+ title.className = 'oe-source-editor__title'
174
+ title.textContent = 'Source'
175
+
176
+ const label = document.createElement('label')
177
+ label.className = 'oe-source-editor__label'
178
+ const labelText = document.createElement('span')
179
+ labelText.textContent = 'Source'
180
+
181
+ /** @type {HTMLInputElement | HTMLTextAreaElement} */
182
+ let field
183
+ if (kind === 'html') {
184
+ const textarea = document.createElement('textarea')
185
+ textarea.rows = 6
186
+ textarea.spellcheck = false
187
+ field = textarea
188
+ } else {
189
+ const input = document.createElement('input')
190
+ input.type = 'url'
191
+ input.inputMode = 'url'
192
+ input.setAttribute('autocomplete', 'url')
193
+ field = input
194
+ }
195
+ field.className = 'oe-source-editor__field'
196
+ field.required = true
197
+ label.append(labelText, field)
198
+
199
+ const error = document.createElement('div')
200
+ error.className = 'oe-source-editor__error'
201
+ error.setAttribute('role', 'alert')
202
+ error.hidden = true
203
+
204
+ const actions = document.createElement('div')
205
+ actions.className = 'oe-source-editor__actions'
206
+
207
+ const cancel = document.createElement('button')
208
+ cancel.type = 'button'
209
+ cancel.className = 'oe-source-editor__button oe-source-editor__button--secondary'
210
+ cancel.textContent = 'Cancel'
211
+
212
+ const submit = document.createElement('button')
213
+ submit.type = 'submit'
214
+ submit.className = 'oe-source-editor__button oe-source-editor__button--primary'
215
+ submit.textContent = 'Insert'
216
+
217
+ actions.append(cancel, submit)
218
+ panel.append(title, label, error, actions)
219
+ root.append(backdrop, panel)
220
+ wrapper.appendChild(root)
221
+
222
+ const layer = createPluginLayer(wrapper, signal)
223
+ /** @type {SourceEditorSurface} */
224
+ let surface
225
+ const destroy = () => {
226
+ const active = activeEditors.get(wrapper)
227
+ if (active?.surface === surface) active.close()
228
+ root.remove()
229
+ if (owned?.get(kind) === surface) owned.delete(kind)
230
+ if (owned?.size === 0) surfaces.delete(wrapper)
231
+ }
232
+ surface = {
233
+ root,
234
+ backdrop,
235
+ panel,
236
+ title,
237
+ labelText,
238
+ field,
239
+ error,
240
+ cancel,
241
+ submit,
242
+ signal,
243
+ layer,
244
+ destroy,
245
+ }
246
+ owned.set(kind, surface)
247
+ signal.addEventListener('abort', destroy, { once: true })
248
+ return surface
249
+ }
250
+
251
+ /**
252
+ * @param {SourceEditorSurface} surface
253
+ * @param {boolean} visible
254
+ * @returns {void}
255
+ */
256
+ function setSurfaceVisible(surface, visible) {
257
+ surface.root.classList.toggle('oe-source-editor--preloaded', !visible)
258
+ surface.root.setAttribute('aria-hidden', String(!visible))
259
+ surface.root.inert = !visible
260
+ }
@@ -5,14 +5,13 @@
5
5
  // =============================================================================
6
6
 
7
7
  import { sanitizeHtml } from '../../core/sanitize.js'
8
- import { resolvePath } from '../../shared/resolvePath.js'
9
8
  import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
10
9
  import { validateSpoilerData } from '../../shared/blockDataValidators.js'
11
10
  import { mapSpoilerTextFields } from '../../shared/mapTextFields.js'
12
11
  import { normalizeTextValue } from '../../shared/textFormat.js'
13
12
  import { READ_ONLY_INTERACTIVE_ATTRIBUTE } from '../../core/constants.js'
14
13
 
15
- const editorStyles = resolvePath('./spoiler.css', import.meta.url)
14
+ const editorStyles = new URL('./spoiler.css', import.meta.url).href
16
15
 
17
16
  // Tabler icon: eye-off
18
17
  const ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.585 10.587a2 2 0 0 0 2.829 2.828"/><path d="M16.681 16.673a8.717 8.717 0 0 1-4.681 1.327c-3.6 0-6.6-2-9-6 1.272-2.12 2.712-3.678 4.32-4.674m2.86-1.146a9.055 9.055 0 0 1 1.82-.18c3.6 0 6.6 2 9 6-.666 1.11-1.379 2.067-2.138 2.87"/><path d="M3 3l18 18"/></svg>'
@@ -1,10 +1,9 @@
1
1
  import { sanitizeHtml } from '../../core/sanitize.js'
2
- import { resolvePath } from '../../shared/resolvePath.js'
3
2
  import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
4
3
  import { validateTableData } from '../../shared/blockDataValidators.js'
5
4
  import { mapTableTextFields } from '../../shared/mapTextFields.js'
6
5
 
7
- const editorStyles = resolvePath('./table.css', import.meta.url)
6
+ const editorStyles = new URL('./table.css', import.meta.url).href
8
7
 
9
8
  // Tabler icon: table
10
9
  const ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14z"/><path d="M3 10h18"/><path d="M10 3v18"/></svg>'
@@ -1,12 +1,11 @@
1
1
  import { sanitizeHtml } from '../../core/sanitize.js'
2
- import { resolvePath } from '../../shared/resolvePath.js'
3
2
  import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
4
3
  import { validateToggleData } from '../../shared/blockDataValidators.js'
5
4
  import { mapToggleTextFields } from '../../shared/mapTextFields.js'
6
5
  import { normalizeTextValue } from '../../shared/textFormat.js'
7
6
  import { READ_ONLY_INTERACTIVE_ATTRIBUTE } from '../../core/constants.js'
8
7
 
9
- const editorStyles = resolvePath('./toggle.css', import.meta.url)
8
+ const editorStyles = new URL('./toggle.css', import.meta.url).href
10
9
 
11
10
  let toggleSequence = 0
12
11
 
@@ -5,13 +5,12 @@
5
5
  // =============================================================================
6
6
 
7
7
  import { sanitizeHtml } from '../../core/sanitize.js'
8
- import { resolvePath } from '../../shared/resolvePath.js'
9
8
  import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
10
9
  import { validateWarningData } from '../../shared/blockDataValidators.js'
11
10
  import { normalizeTextValue } from '../../shared/textFormat.js'
12
11
  import { mapWarningTextFields } from '../../shared/mapTextFields.js'
13
12
 
14
- const editorStyles = resolvePath('./warning.css', import.meta.url)
13
+ const editorStyles = new URL('./warning.css', import.meta.url).href
15
14
 
16
15
  // Tabler icon: alert-triangle
17
16
  const ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 9v4"/><path d="M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636-2.87l-8.106-13.536a1.914 1.914 0 0 0-3.274 0z"/><path d="M12 16h.01"/></svg>'
@@ -2,7 +2,6 @@
2
2
  import { InvalidBlockDataError, UnknownBlockTypeError } from './errors.js'
3
3
  import { createInlineParser } from './inline.js'
4
4
  import { createDefaultRenderers, getSupportedBlockTypes } from './renderers/index.js'
5
- import { resolvePath } from '../shared/resolvePath.js'
6
5
  import { deserializeInlineHtml } from '../shared/inlineMarshal.js'
7
6
  import { cloneEditorData } from '../shared/cloneEditorData.js'
8
7
  import defaultLocale from './locale/en.js'
@@ -11,7 +10,7 @@ import { validateKnownBlockData } from '../shared/blockDataValidators.js'
11
10
  import { normalizeKnownBlockData } from '../shared/blockDataNormalizers.js'
12
11
  import { normalizeTextAlign } from '../shared/textFormat.js'
13
12
 
14
- const baseCssUrl = resolvePath('./styles/base.css', import.meta.url)
13
+ const baseCssUrl = new URL('./styles/base.css', import.meta.url).href
15
14
 
16
15
  /**
17
16
  * Renders Rector document blocks to DOM elements.
@@ -1,12 +1,11 @@
1
1
  // @ts-check
2
- import { resolvePath } from '../../../shared/resolvePath.js'
3
2
  import { getFileIcon, getExtension, formatSize, EXT_COLORS } from '../../../shared/fileUtils.js'
4
3
  import { sanitizeDownloadUrl, setSafeUrlAttribute } from '../../../shared/sanitize/sanitizeUrl.js'
5
4
  import { loadZipRuntime } from '../../../shared/zipRuntime.js'
6
5
 
7
6
  let groupSequence = 0
8
7
 
9
- const styles = resolvePath('./styles.css', import.meta.url)
8
+ const styles = new URL('./styles.css', import.meta.url).href
10
9
 
11
10
  const ICON_CHEVRON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'
12
11
  const ICON_FILE_DEFAULT = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2"/></svg>'