@ckeditor/ckeditor5-dev-manual-server 56.6.0 → 57.0.0-alpha.1

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.
@@ -3,4 +3,11 @@
3
3
  * For licensing, see LICENSE.md.
4
4
  */
5
5
  import type { ManualPageEntry } from "./types.js";
6
+ /**
7
+ * Discovers manual test pages. A manual test is a single full HTML document named
8
+ * `<slug>.manual.html` located under a package's `tests/manual/` directory. Plain `.html`
9
+ * files are treated as static fixtures and are never collected here.
10
+ *
11
+ * The `patterns` are package root globs, for example `packages/*`.
12
+ */
6
13
  export declare function collectManualPages(patterns: Array<string>, workspaceRoot: string): Map<string, ManualPageEntry>;
@@ -3,7 +3,6 @@
3
3
  * For licensing, see LICENSE.md.
4
4
  */
5
5
  import type { Plugin } from "vite";
6
- export type { ManualData } from "./types.js";
7
6
  export interface ManualTestsPluginOptions {
8
7
  paths: Array<string>;
9
8
  include?: Array<string>;
@@ -2,17 +2,8 @@
2
2
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md.
4
4
  */
5
- export interface ManualData {
6
- displayName: string;
7
- instructionsHtml: string;
8
- packageName: string;
9
- }
10
5
  export interface ManualPageEntry {
11
- displayName: string;
12
6
  htmlFilePath: string;
13
- instructionsFilePath?: string;
14
7
  packageName: string;
15
- scriptFilePath: string;
16
8
  slug: string;
17
9
  }
18
- export type ManualTestAssetExtension = "html" | "js" | "md" | "ts";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+ import type { Plugin } from "vite";
6
+ export interface ManualStaticAssetsPluginOptions {
7
+ paths: Array<string>;
8
+ include?: Array<string>;
9
+ }
10
+ export declare function manualStaticAssetsPlugin(options: ManualStaticAssetsPluginOptions): Plugin;
@@ -6,5 +6,4 @@ import type { IncomingMessage, ServerResponse } from "node:http";
6
6
  type ManualStaticAssetsMiddleware = (request: IncomingMessage, response: ServerResponse, next: () => void) => void;
7
7
  export declare function collectManualStaticAssets(patterns: Array<string>, workspaceRoot: string): Map<string, string>;
8
8
  export declare function createManualStaticAssetsMiddleware(collectStaticAssets: () => Map<string, string>): ManualStaticAssetsMiddleware;
9
- export declare function getManualStaticAssetFilePath(requestUrl: string | undefined, staticAssets: Map<string, string>): string | null;
10
9
  export {};
@@ -3,4 +3,4 @@
3
3
  * For licensing, see LICENSE.md.
4
4
  */
5
5
  import type { Plugin } from "vite";
6
- export declare function rawHtmlPlugin(): Plugin;
6
+ export declare function rawSvgPlugin(): Plugin;
package/dist/utils.d.ts CHANGED
@@ -14,6 +14,14 @@ export declare function cacheValue<T>(compute: () => T): CachedValue<T>;
14
14
  * Stringifies the values of the given object.
15
15
  */
16
16
  export declare function stringifyValues(obj: Record<string, unknown>): Record<string, string>;
17
+ export declare function normalizePackageName(packageName: string): string;
18
+ /**
19
+ * Builds a predicate that decides whether a package is part of the `include` selection. When the
20
+ * selection is empty every package is included. Package names are matched by their normalized form,
21
+ * so both short (`case-change`) and full (`ckeditor5-case-change`) names work. A `null` package name
22
+ * (an asset without a package segment) is only kept when the selection is empty.
23
+ */
24
+ export declare function createPackageNameFilter(includePackageNames: Array<string>): (packageName: string | null) => boolean;
17
25
  export declare function toPublicFilePath(filePath: string, workspaceRoot: string): string;
18
26
  export declare function toPublicSpecifier(relativeFilePath: string): string;
19
27
  export declare function toPosixPath(value: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-manual-server",
3
- "version": "56.6.0",
3
+ "version": "57.0.0-alpha.1",
4
4
  "description": "Used to extend Vite to create a manual test server for CKEditor 5.",
5
5
  "keywords": [],
6
6
  "author": "CKSource (http://cksource.com/)",
@@ -25,14 +25,6 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "@ckeditor/ckeditor5-inspector": "^5.0.1",
28
- "@parse5/tools": "^0.7.0",
29
- "hast-util-to-html": "^9.0.5",
30
- "parse5": "^8.0.1",
31
- "remark-gfm": "^4.0.1",
32
- "remark-parse": "^11.0.0",
33
- "remark-rehype": "^11.1.2",
34
- "unified": "^11.0.5",
35
- "vite": "^8.0.13",
36
- "vite-svg-loader": "^5.1.1"
28
+ "vite": "^8.1.0"
37
29
  }
38
30
  }
package/theme/catalog.css CHANGED
@@ -215,19 +215,9 @@ a {
215
215
  .pkg__test-name {
216
216
  flex: 1;
217
217
  min-width: 0;
218
- font-size: 13px;
219
- font-weight: 500;
220
- overflow: hidden;
221
- text-overflow: ellipsis;
222
- white-space: nowrap;
223
- }
224
-
225
- .pkg__test-slug {
226
- flex-shrink: 0;
227
- max-width: 50%;
228
218
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
229
- font-size: 11px;
230
- color: var(--text-subtle);
219
+ font-size: 12px;
220
+ font-weight: 500;
231
221
  overflow: hidden;
232
222
  text-overflow: ellipsis;
233
223
  white-space: nowrap;
@@ -245,8 +235,4 @@ a {
245
235
  .app__list {
246
236
  grid-template-columns: 1fr;
247
237
  }
248
-
249
- .pkg__test-slug {
250
- display: none;
251
- }
252
238
  }
@@ -47,7 +47,6 @@
47
47
  <li class="pkg__item">
48
48
  <a class="pkg__link">
49
49
  <span class="pkg__test-name"></span>
50
- <span class="pkg__test-slug"></span>
51
50
  </a>
52
51
  </li>
53
52
  </template>
package/theme/catalog.ts CHANGED
@@ -97,8 +97,7 @@ function filterEntries( entries: Array<ManualTestEntry>, query: string ): Array<
97
97
 
98
98
  return entries.filter( entry =>
99
99
  entry.packageName.toLowerCase().includes( normalized ) ||
100
- entry.slug.toLowerCase().includes( normalized ) ||
101
- entry.displayName.toLowerCase().includes( normalized )
100
+ entry.slug.toLowerCase().includes( normalized )
102
101
  );
103
102
  }
104
103
 
@@ -144,8 +143,7 @@ function renderTestItem( entry: ManualTestEntry ): HTMLElement {
144
143
  const link = testItem.querySelector<HTMLAnchorElement>( '.pkg__link' )!;
145
144
 
146
145
  link.href = entry.href;
147
- testItem.querySelector<HTMLElement>( '.pkg__test-name' )!.textContent = entry.displayName;
148
- testItem.querySelector<HTMLElement>( '.pkg__test-slug' )!.textContent = entry.slug;
146
+ testItem.querySelector<HTMLElement>( '.pkg__test-name' )!.textContent = entry.slug;
149
147
 
150
148
  return testItem;
151
149
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ /* Styles for the shadow DOM hosting the "source changed" refresh prompt (manual-bootstrap.ts). */
7
+
8
+ .refresh-prompt {
9
+ position: fixed;
10
+ top: calc( var( --ck-manual-header-height, 44px ) / 1.25 );
11
+ left: 50%;
12
+ z-index: 100000;
13
+ box-sizing: border-box;
14
+ padding: 8px 14px;
15
+ border: 1px solid #bfdbfe;
16
+ border-radius: 999px;
17
+ font: 600 12px/1.25 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
18
+ color: #1d4ed8;
19
+ background: #dbeafe;
20
+ cursor: pointer;
21
+ opacity: 0;
22
+ pointer-events: none;
23
+ transform: translate(-50%, calc( -100% - var( --ck-manual-header-height, 44px ) ) );
24
+ transition: transform 0.18s ease-out, opacity 0.18s ease-out, background 0.1s;
25
+ }
26
+
27
+ .refresh-prompt--visible {
28
+ opacity: 1;
29
+ pointer-events: auto;
30
+ transform: translate(-50%, -50%);
31
+ }
32
+
33
+ .refresh-prompt:hover {
34
+ background: #bfdbfe;
35
+ }
36
+
37
+ .refresh-prompt:active {
38
+ background: #93c5fd;
39
+ }
40
+
41
+ .refresh-prompt:focus-visible {
42
+ outline: 2px solid #2563eb;
43
+ outline-offset: 1px;
44
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ import CKEditorInspector from '@ckeditor/ckeditor5-inspector';
7
+
8
+ import promptStyles from './manual-bootstrap.css?inline';
9
+
10
+ declare const LICENSE_KEY: string;
11
+
12
+ interface ViteHotContextLike {
13
+ on( event: string, callback: () => void ): void;
14
+ }
15
+
16
+ const MANUAL_REFRESH_EVENT_NAME = 'ckeditor5-manual:refresh-available';
17
+
18
+ const globalTarget = window as any;
19
+
20
+ /**
21
+ * Invisible environment setup injected by the manual test server into EVERY manual test page
22
+ * (`*.manual.html`), with or without the `<ck-manual-header>` chrome:
23
+ *
24
+ * - sets the global license key so tests do not have to pass `licenseKey` explicitly;
25
+ * - resets the `window.editor` named DOM property and auto-attaches the CKEditor inspector;
26
+ * - renders the "source changed" refresh prompt driven by the dev server (`refreshPlugin`).
27
+ *
28
+ * The refresh prompt is part of the dev-server contract, not test chrome, so it belongs here.
29
+ */
30
+
31
+ // In direct HTML manual tests, `id="editor"` creates `window.editor` as a named DOM property.
32
+ // Reset it so manual tests can safely reuse `window.editor` for the editor instance.
33
+ if ( globalTarget.editor instanceof Element ) {
34
+ globalTarget.editor = null;
35
+ }
36
+
37
+ globalTarget.CKEditorInspector ||= CKEditorInspector;
38
+
39
+ if ( typeof LICENSE_KEY != 'undefined' ) {
40
+ globalTarget.CKEDITOR_GLOBAL_LICENSE_KEY ||= LICENSE_KEY;
41
+ }
42
+
43
+ autoAttachInspector();
44
+ setUpRefreshPrompt();
45
+
46
+ function autoAttachInspector(): void {
47
+ let editor = globalTarget.editor;
48
+
49
+ Object.defineProperty( globalTarget, 'editor', {
50
+ configurable: true,
51
+ set( value ) {
52
+ editor = value;
53
+
54
+ if ( editor ) {
55
+ CKEditorInspector.attach( editor );
56
+ }
57
+ },
58
+ get() {
59
+ return editor;
60
+ }
61
+ } );
62
+ }
63
+
64
+ /**
65
+ * Shows a refresh prompt when the dev server reports source changes (see `refreshPlugin`).
66
+ */
67
+ function setUpRefreshPrompt(): void {
68
+ const hot = ( import.meta as ImportMeta & { hot?: ViteHotContextLike } ).hot;
69
+
70
+ if ( !hot ) {
71
+ return;
72
+ }
73
+
74
+ const button = document.createElement( 'button' );
75
+
76
+ button.type = 'button';
77
+ button.className = 'refresh-prompt';
78
+ button.textContent = 'Source changed. Click here to refresh the page.';
79
+ button.tabIndex = -1;
80
+ button.setAttribute( 'aria-hidden', 'true' );
81
+ button.addEventListener( 'click', () => window.location.reload() );
82
+
83
+ // Render the prompt inside a shadow host so its styles do not leak into the test page.
84
+ const host = document.createElement( 'div' );
85
+ const shadow = host.attachShadow( { mode: 'open' } );
86
+ const sheet = new CSSStyleSheet();
87
+
88
+ sheet.replaceSync( promptStyles );
89
+ shadow.adoptedStyleSheets = [ sheet ];
90
+ shadow.append( button );
91
+
92
+ const appendHost = () => document.body.append( host );
93
+
94
+ if ( document.body ) {
95
+ appendHost();
96
+ } else {
97
+ document.addEventListener( 'DOMContentLoaded', appendHost );
98
+ }
99
+
100
+ hot.on( MANUAL_REFRESH_EVENT_NAME, () => {
101
+ button.removeAttribute( 'aria-hidden' );
102
+ button.removeAttribute( 'tabindex' );
103
+ button.classList.add( 'refresh-prompt--visible' );
104
+ } );
105
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ /* Styles for the shadow DOM of the <ck-manual-header> component (test chrome). */
7
+
8
+ :host {
9
+ display: block;
10
+ --ck-manual-header-height: 44px;
11
+ }
12
+
13
+ .bar {
14
+ position: fixed;
15
+ top: 0;
16
+ left: 0;
17
+ right: 0;
18
+ z-index: 99999;
19
+ display: flex;
20
+ align-items: stretch;
21
+ box-sizing: border-box;
22
+ padding: 6px 8px;
23
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
24
+ font-size: 12px;
25
+ line-height: 1.25;
26
+ color: #0a0a0a;
27
+ background: rgb(255 255 255 / 85%);
28
+ backdrop-filter: saturate(180%) blur(20px);
29
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
30
+ border-bottom: 1px solid rgb(0 0 0 / 8%);
31
+ box-shadow: 0 1px 2px rgb(0 0 0 / 4%);
32
+ }
33
+
34
+ .pill {
35
+ display: inline-flex;
36
+ align-items: center;
37
+ padding: 8px 14px;
38
+ border: 0;
39
+ background: transparent;
40
+ border-radius: 999px;
41
+ text-decoration: none;
42
+ color: inherit;
43
+ font: inherit;
44
+ font-weight: 500;
45
+ white-space: nowrap;
46
+ transition: background 0.1s;
47
+ cursor: pointer;
48
+ }
49
+
50
+ .pill:hover {
51
+ background: rgb(0 0 0 / 5%);
52
+ }
53
+
54
+ .pill:active {
55
+ background: rgb(0 0 0 / 10%);
56
+ }
57
+
58
+ .pill:focus-visible {
59
+ outline: 2px solid rgb(0 0 0 / 30%);
60
+ outline-offset: 1px;
61
+ }
62
+
63
+ .divider {
64
+ flex-shrink: 0;
65
+ align-self: center;
66
+ width: 1px;
67
+ height: 16px;
68
+ background: rgb(0 0 0 / 10%);
69
+ margin: 0 2px;
70
+ }
71
+
72
+ .label {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ flex: 1 1 auto;
76
+ gap: 10px;
77
+ padding: 8px 14px;
78
+ overflow: hidden;
79
+ min-width: 0;
80
+ }
81
+
82
+ .label__name {
83
+ font-weight: 600;
84
+ overflow: hidden;
85
+ text-overflow: ellipsis;
86
+ white-space: nowrap;
87
+ }
88
+
89
+ .label__package {
90
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
91
+ font-weight: 400;
92
+ font-size: 11px;
93
+ opacity: 0.55;
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ }
98
+
99
+ :host( :not( [ has-instructions ] ) ) .instructions-only {
100
+ display: none;
101
+ }
102
+
103
+ @media (max-width: 600px) {
104
+ .bar {
105
+ font-size: 11px;
106
+ }
107
+
108
+ .label__package {
109
+ display: none;
110
+ }
111
+
112
+ .label__name {
113
+ max-width: 20ch;
114
+ }
115
+ }
116
+
117
+ .panel {
118
+ --panel-width: min(420px, 45vw);
119
+ --panel-transition-duration: 0.18s;
120
+
121
+ position: fixed;
122
+ top: var(--ck-manual-header-height);
123
+ right: 0;
124
+ bottom: 0;
125
+ z-index: 99998;
126
+ box-sizing: border-box;
127
+ width: 0;
128
+ background: #fff;
129
+ color: #0a0a0a;
130
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
131
+ line-height: 1.6;
132
+ overflow: hidden;
133
+ transition: width var(--panel-transition-duration) ease-out;
134
+ }
135
+
136
+ .panel--open {
137
+ width: var(--panel-width);
138
+ border-left: 1px solid rgb(0 0 0 / 8%);
139
+ box-shadow: -8px 0 24px rgb(0 0 0 / 8%);
140
+ }
141
+
142
+ .panel__inner {
143
+ display: flex;
144
+ flex-direction: column;
145
+ width: var(--panel-width);
146
+ height: 100%;
147
+ }
148
+
149
+ .panel__header {
150
+ display: flex;
151
+ align-items: flex-start;
152
+ justify-content: space-between;
153
+ gap: 16px;
154
+ padding: 20px 24px;
155
+ border-bottom: 1px solid rgb(0 0 0 / 8%);
156
+ flex-shrink: 0;
157
+ }
158
+
159
+ .panel__titles {
160
+ min-width: 0;
161
+ }
162
+
163
+ .panel__title {
164
+ margin: 0 0 4px;
165
+ font-size: 18px;
166
+ font-weight: 700;
167
+ letter-spacing: -0.01em;
168
+ line-height: 1.3;
169
+ overflow: hidden;
170
+ text-overflow: ellipsis;
171
+ white-space: nowrap;
172
+ }
173
+
174
+ .panel__package {
175
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
176
+ font-size: 12px;
177
+ color: #737373;
178
+ }
179
+
180
+ .panel__close {
181
+ flex-shrink: 0;
182
+ display: inline-flex;
183
+ align-items: center;
184
+ justify-content: center;
185
+ width: 32px;
186
+ height: 32px;
187
+ padding: 0;
188
+ border: 0;
189
+ border-radius: 8px;
190
+ background: transparent;
191
+ color: #737373;
192
+ cursor: pointer;
193
+ transition: background 0.1s, color 0.1s;
194
+ }
195
+
196
+ .panel__close:hover {
197
+ background: rgb(0 0 0 / 5%);
198
+ color: #0a0a0a;
199
+ }
200
+
201
+ .panel__close:focus-visible {
202
+ outline: 2px solid rgb(0 0 0 / 30%);
203
+ outline-offset: 1px;
204
+ }
205
+
206
+ .panel__body {
207
+ padding: 20px 24px 28px;
208
+ font-size: 14px;
209
+ overflow-y: auto;
210
+ min-height: 0;
211
+ }
212
+
213
+ @media (max-width: 800px) {
214
+ .panel {
215
+ --panel-width: min(360px, 70vw);
216
+ }
217
+ }