umberto 10.6.1 → 10.7.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.
- package/CHANGELOG.md +27 -14
- package/package.json +5 -4
- package/scripts/utils/pug-to-xml-binding/render-xml-pug-components-in-markdown.cjs +45 -8
- package/src/api-builder/api-builder.js +2 -0
- package/src/helpers/rewrite-public-source-url.js +42 -0
- package/src/tasks/get-project-config.js +39 -0
- package/themes/umberto/layout/gloria/_api-docs/_mixin/_api-see-source.pug +2 -1
- package/themes/umberto/layout/gloria/_components/tabs/index.pug +1 -0
- package/themes/umberto/layout/gloria/_modules/kapa/index.pug +21 -0
- package/themes/umberto/src/gloria/css/layout/_base.scss +4 -0
- package/themes/umberto/src/gloria/js/components/hash-link.js +30 -0
- package/themes/umberto/src/gloria/js/modules/header.js +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
## [10.7.1](https://github.com/cksource/umberto/compare/v10.7.0...v10.7.1) (July 15, 2026)
|
|
5
|
+
|
|
6
|
+
### Bug fixes
|
|
7
|
+
|
|
8
|
+
* Fixed source links for CKEditor 5 packages in API documentation so they now point to the public repository. Links for the latest documentation point to the `master` branch, and links for LTS documentation point to the matching `release-v*` branch.
|
|
9
|
+
|
|
10
|
+
### Other changes
|
|
11
|
+
|
|
12
|
+
* Improved the performance of the documentation build. Pug templates of XML components used in Markdown are now compiled once and reused across pages, instead of being recompiled for every component occurrence. This shortens the page generation phase by about 20%.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## [10.7.0](https://github.com/cksource/umberto/compare/v10.6.1...v10.7.0) (July 2, 2026)
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* Enabled MCP install menu in the Kapa widget.
|
|
20
|
+
|
|
21
|
+
### Bug fixes
|
|
22
|
+
|
|
23
|
+
* Fixed scrolling to a section in the Gloria theme when navigating to an anchor without a full page reload — for example, changing the anchor in the address bar, using the browser's back/forward buttons, or opening a link to a section on the current page. The target heading is now offset below the sticky header instead of being hidden beneath it.
|
|
24
|
+
* Moved `chokidar` from `devDependencies` to `dependencies`. It is imported at runtime by the file watcher (`src/tasks/watcher.js`), which ships with the package, so it must be installed for consumers. Previously it resolved only by chance through a hoisted copy in the consumer's tree, which fails when the consumer enables pnpm's `enableGlobalVirtualStore` and the package is linked from the global store, outside the consumer's `node_modules`.
|
|
25
|
+
|
|
26
|
+
### Other changes
|
|
27
|
+
|
|
28
|
+
* The tabbed content component now sets a `data-tab-label` attribute on each tab panel, carrying the label of its tab. This exposes the label to tools that process the rendered HTML, where it otherwise lives only inside the tab button.
|
|
29
|
+
|
|
30
|
+
|
|
4
31
|
## [10.6.1](https://github.com/cksource/umberto/compare/v10.6.0...v10.6.1) (June 18, 2026)
|
|
5
32
|
|
|
6
33
|
### Other changes
|
|
@@ -30,20 +57,6 @@ Changelog
|
|
|
30
57
|
|
|
31
58
|
* Changed the call for feedback title from `<h3>` to `<div>` to prevent it from appearing in the table of contents.
|
|
32
59
|
|
|
33
|
-
|
|
34
|
-
## [10.5.0](https://github.com/cksource/umberto/compare/v10.4.1...v10.5.0) (April 14, 2026)
|
|
35
|
-
|
|
36
|
-
### Features
|
|
37
|
-
|
|
38
|
-
* Added the Call for feedback component for documentation pages.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## [10.4.1](https://github.com/cksource/umberto/compare/v10.4.0...v10.4.1) (April 13, 2026)
|
|
42
|
-
|
|
43
|
-
### Bug fixes
|
|
44
|
-
|
|
45
|
-
* Fixed URL in the dropdown for LTS item to have absolute URL to pass documentation validation.
|
|
46
|
-
|
|
47
60
|
---
|
|
48
61
|
|
|
49
62
|
To see all releases, visit the [release page](https://github.com/cksource/umberto/releases).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "umberto",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.7.1",
|
|
4
4
|
"description": "CKSource Documentation builder",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
"CHANGELOG.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@babel/core": "^7.
|
|
19
|
-
"@babel/preset-env": "^7.29.
|
|
18
|
+
"@babel/core": "^7.29.7",
|
|
19
|
+
"@babel/preset-env": "^7.29.7",
|
|
20
20
|
"@minify-html/node": "^0.17.1",
|
|
21
21
|
"@vscode/markdown-it-katex": "^1.1.2",
|
|
22
22
|
"babel-loader": "^10.0.0",
|
|
23
|
+
"chokidar": "^4.0.3",
|
|
23
24
|
"css-select": "^6.0.0",
|
|
24
25
|
"dom-serializer": "^2.0.0",
|
|
25
26
|
"domhandler": "^5.0.3",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"jquery": "~3.7.1",
|
|
41
42
|
"js-beautify": "^1.14.4",
|
|
42
43
|
"lodash": "^4.17.21",
|
|
43
|
-
"markdown-it": "^14.
|
|
44
|
+
"markdown-it": "^14.2.0",
|
|
44
45
|
"markdown-it-abbr": "^1.0.4",
|
|
45
46
|
"markdown-it-deflist": "^2.1.0",
|
|
46
47
|
"markdown-it-emoji": "^2.0.2",
|
|
@@ -9,6 +9,39 @@ const transformXMLTreeToPug = require( './transform-xml-tree-to-pug.cjs' );
|
|
|
9
9
|
const XMLComponentsParser = require( './parser/xml-components-parser.cjs' );
|
|
10
10
|
const { walkXMLTree, TreeWalkActions } = require( './parser/walk-xml-tree.cjs' );
|
|
11
11
|
|
|
12
|
+
// Compiled Pug templates of component mixin calls, keyed by the generated template text.
|
|
13
|
+
// Compiling a template is expensive because Pug lexes and parses the whole `include` tree
|
|
14
|
+
// on every compilation, and the same mixin calls repeat across pages. The compiled function
|
|
15
|
+
// is not bound to any locals, so it can be safely reused with per-page locals.
|
|
16
|
+
//
|
|
17
|
+
// The cache lives for the whole process, like the cache in `createPrerenderPugTemplate()`,
|
|
18
|
+
// so changes to component Pug files require a restart in the watch mode.
|
|
19
|
+
const compiledTemplateCache = new Map();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compiles a Pug template through the Hexo Pug renderer, reusing previously compiled templates.
|
|
23
|
+
*
|
|
24
|
+
* Falls back to `null` when the renderer does not support compilation, in which case the caller
|
|
25
|
+
* should render through `hexo.render.renderSync()`.
|
|
26
|
+
*/
|
|
27
|
+
function getCompiledPugTemplate( hexo, text, path ) {
|
|
28
|
+
const cacheKey = `${ path }\n${ text }`;
|
|
29
|
+
let template = compiledTemplateCache.get( cacheKey );
|
|
30
|
+
|
|
31
|
+
if ( !template ) {
|
|
32
|
+
const pugRenderer = hexo.render.renderer?.get( 'pug' );
|
|
33
|
+
|
|
34
|
+
if ( !pugRenderer?.compile ) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
template = pugRenderer.compile( { text, path } );
|
|
39
|
+
compiledTemplateCache.set( cacheKey, template );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return template;
|
|
43
|
+
}
|
|
44
|
+
|
|
12
45
|
/**
|
|
13
46
|
* Slices XML components from Markdown content and transforms them to Pug format.
|
|
14
47
|
*
|
|
@@ -93,14 +126,18 @@ function renderXMLPugComponentsInMarkdown( xml, options ) {
|
|
|
93
126
|
let finalReplacement = null;
|
|
94
127
|
|
|
95
128
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
129
|
+
const template = getCompiledPugTemplate( hexo, content, basePath );
|
|
130
|
+
|
|
131
|
+
finalReplacement = template ?
|
|
132
|
+
template( locals ).trim() :
|
|
133
|
+
hexo.render.renderSync(
|
|
134
|
+
{
|
|
135
|
+
engine: 'pug',
|
|
136
|
+
text: content,
|
|
137
|
+
path: basePath
|
|
138
|
+
},
|
|
139
|
+
locals
|
|
140
|
+
).trim();
|
|
104
141
|
} catch ( error ) {
|
|
105
142
|
console.error( error );
|
|
106
143
|
console.error( `Error rendering component "${ node.name }" in file ${ sourceName }` );
|
|
@@ -19,6 +19,7 @@ import { getShortModulePath } from '../helpers/get-short-module-path.js';
|
|
|
19
19
|
import { getDocsearchConfig as getDocSearchConfig } from '../helpers/get-docsearch-config.js';
|
|
20
20
|
import { splitLongname } from '../helpers/split-longname.js';
|
|
21
21
|
import { getIssueUrl, getFullGithubLink } from '../helpers/github-url.js';
|
|
22
|
+
import { rewritePublicSourceUrl } from '../helpers/rewrite-public-source-url.js';
|
|
22
23
|
import getReportIssueWidgetUrl from '../../scripts/utils/getreportissuewidgeturl.cjs';
|
|
23
24
|
import { randomId } from '../../scripts/utils/random-id.cjs';
|
|
24
25
|
import parseHref from '../../scripts/utils/parse-href.cjs';
|
|
@@ -521,6 +522,7 @@ export class ApiBuilder {
|
|
|
521
522
|
groups,
|
|
522
523
|
seeSourceRepositoryUrl: this._projectConfig.github !== undefined ? this._projectConfig.github.url : realImportPath,
|
|
523
524
|
getLinkToSource: getFullGithubLink.bind( this ),
|
|
525
|
+
rewritePublicSourceUrl: url => rewritePublicSourceUrl( url, this._projectConfig.ckeditor5SourceLinkBranch ),
|
|
524
526
|
docSearchConfig: getDocSearchConfig( this._docSearch, {
|
|
525
527
|
groups: this._groups,
|
|
526
528
|
slug: this._projectSlug,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2017-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Rewrites CKEditor 5 public package source links generated from the commercial repository checkout.
|
|
8
|
+
*
|
|
9
|
+
* @param {String} sourceUrl
|
|
10
|
+
* @param {String} sourceBranch
|
|
11
|
+
* @returns {String}
|
|
12
|
+
*/
|
|
13
|
+
export function rewritePublicSourceUrl( sourceUrl, sourceBranch ) {
|
|
14
|
+
if ( !sourceUrl || !sourceBranch ) {
|
|
15
|
+
return sourceUrl;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let url;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
url = new URL( sourceUrl );
|
|
22
|
+
} catch {
|
|
23
|
+
return sourceUrl;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ( url.hostname !== 'github.com' ) {
|
|
27
|
+
return sourceUrl;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const match = url.pathname.match( /^\/[^/]+\/ckeditor5-commercial\/blob\/[^/]+\/external\/ckeditor5\/(.+)$/ );
|
|
31
|
+
|
|
32
|
+
if ( !match ) {
|
|
33
|
+
return sourceUrl;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return [
|
|
37
|
+
'https://github.com/ckeditor/ckeditor5',
|
|
38
|
+
'blob',
|
|
39
|
+
sourceBranch,
|
|
40
|
+
match[ 1 ]
|
|
41
|
+
].join( '/' ) + url.search + url.hash;
|
|
42
|
+
}
|
|
@@ -92,6 +92,7 @@ export const getProjectConfig = async ( rootPath, options = {} ) => {
|
|
|
92
92
|
validateConfiguration( config );
|
|
93
93
|
|
|
94
94
|
config.version = getProjectVersion( rootPath );
|
|
95
|
+
config.ckeditor5SourceLinkBranch = getCKEditor5SourceLinkBranch( rootPath, config );
|
|
95
96
|
|
|
96
97
|
parseGitHubUrls( config, rootPath );
|
|
97
98
|
|
|
@@ -304,6 +305,44 @@ function getProjectVersion( rootPath ) {
|
|
|
304
305
|
return version || 'latest';
|
|
305
306
|
}
|
|
306
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Returns CKEditor 5 source link branch based on LTS metadata from `package.json`.
|
|
310
|
+
*
|
|
311
|
+
* @param {String} rootPath
|
|
312
|
+
* @param {Object} config
|
|
313
|
+
* @param {String} config.slug
|
|
314
|
+
* @param {String} config.version
|
|
315
|
+
* @return {String|undefined}
|
|
316
|
+
*/
|
|
317
|
+
function getCKEditor5SourceLinkBranch( rootPath, config ) {
|
|
318
|
+
if ( config.slug !== 'ckeditor5' ) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const packageJson = getPackageJson( rootPath );
|
|
323
|
+
const ltsVersions = packageJson ? packageJson[ 'ck-lts-versions' ] : null;
|
|
324
|
+
const releaseLine = getReleaseLine( config.version );
|
|
325
|
+
const ltsReleaseLines = Array.isArray( ltsVersions ) ? ltsVersions.map( Number ) : null;
|
|
326
|
+
|
|
327
|
+
if ( !ltsReleaseLines || releaseLine === null ) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const isLtsVersion = ltsReleaseLines.includes( releaseLine );
|
|
332
|
+
|
|
333
|
+
return isLtsVersion ? `release-v${ releaseLine }` : 'master';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @param {String} version
|
|
338
|
+
* @return {Number|null}
|
|
339
|
+
*/
|
|
340
|
+
function getReleaseLine( version ) {
|
|
341
|
+
const match = version.match( /^(\d+)\./ );
|
|
342
|
+
|
|
343
|
+
return match ? Number( match[ 1 ] ) : null;
|
|
344
|
+
}
|
|
345
|
+
|
|
307
346
|
/**
|
|
308
347
|
* Tries to find a URL to the repository based on the value in `package.json`, under the "repository" object.
|
|
309
348
|
*
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
mixin seeSource( item )
|
|
2
2
|
//- Do not render "See source" button if the module or the parsed item (e.g. a function) has the `@skipsource` annotation
|
|
3
3
|
if ( projectLocals.seeSourceRepositoryUrl && item.file && !( data.skipSource || item.skipSource ) )
|
|
4
|
-
- const
|
|
4
|
+
- const sourceLink = item.file.url || projectLocals.getLinkToSource( projectLocals.seeSourceRepositoryUrl, item.file );
|
|
5
|
+
- const link = projectLocals.rewritePublicSourceUrl( sourceLink );
|
|
5
6
|
|
|
6
7
|
if link
|
|
7
8
|
.see-source
|
|
@@ -70,6 +70,7 @@ mixin tabs({ id: tabsId, ariaLabel = 'Tabbed content', className, align = 'left'
|
|
|
70
70
|
aria-labelledby=`${idPrefix}-${ id }-tab`
|
|
71
71
|
aria-hidden=tab.active ? 'false' : 'true'
|
|
72
72
|
data-tab-id=id
|
|
73
|
+
data-tab-label=tab.label
|
|
73
74
|
class={
|
|
74
75
|
[className]: !!className,
|
|
75
76
|
'is-spaced': tab.spaced,
|
|
@@ -139,11 +139,32 @@ mixin load-kapa-script( kapa )
|
|
|
139
139
|
return projectConfig.sourceGroupIds.join( ',' );
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
function getMcpServerUrl() {
|
|
143
|
+
const defaultMcpServerUrl = kapa.default.mcpServerUrl || null;
|
|
144
|
+
|
|
145
|
+
if ( !projectConfig ) {
|
|
146
|
+
return defaultMcpServerUrl;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if ( !projectConfig.mcpServerUrl ) {
|
|
150
|
+
return defaultMcpServerUrl;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return projectConfig.mcpServerUrl;
|
|
154
|
+
}
|
|
155
|
+
|
|
142
156
|
attributes[ 'data-modal-title' ] = getModalTitle( projectConfig );
|
|
143
157
|
attributes[ 'data-modal-ask-ai-input-placeholder' ] = getQuestionPlaceholder();
|
|
144
158
|
attributes[ 'data-modal-example-questions' ] = getExampleQuestions();
|
|
145
159
|
attributes[ 'data-source-group-ids-include' ] = getSourceGroupIds();
|
|
146
160
|
|
|
161
|
+
const mcpServerUrl = getMcpServerUrl();
|
|
162
|
+
|
|
163
|
+
if ( mcpServerUrl ) {
|
|
164
|
+
attributes[ 'data-mcp-enabled' ] = 'true';
|
|
165
|
+
attributes[ 'data-mcp-server-url' ] = mcpServerUrl;
|
|
166
|
+
}
|
|
167
|
+
|
|
147
168
|
script&attributes(attributes)
|
|
148
169
|
|
|
149
170
|
style#custom_kapa_styles
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
height: 100%;
|
|
6
6
|
overscroll-behavior-y: none; /* Prevent vertical bounce, allow horizontal trackpad gestures. */
|
|
7
7
|
overflow-x: hidden;
|
|
8
|
+
|
|
9
|
+
/* Offset native fragment navigation (anchor jumps, pasting a hash URL) below the
|
|
10
|
+
sticky header. `--header-height` is kept up to date by the header module. */
|
|
11
|
+
scroll-padding-top: calc(var(--header-height, 0px) + 32px);
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
body {
|
|
@@ -18,6 +18,10 @@ export class HashLink extends BaseComponent {
|
|
|
18
18
|
// Handle initial hash link navigation if present in the URL
|
|
19
19
|
this._handleInitialHash();
|
|
20
20
|
|
|
21
|
+
// Handle hash changes on an already-loaded page (e.g. editing the hash in the
|
|
22
|
+
// address bar or browser back/forward navigation).
|
|
23
|
+
this._handleHashChange();
|
|
24
|
+
|
|
21
25
|
// Add click event listener to handle hash link clicks
|
|
22
26
|
document.addEventListener( 'click', event => {
|
|
23
27
|
const target = event.target.closest( 'a[href^="#"]' );
|
|
@@ -57,4 +61,30 @@ export class HashLink extends BaseComponent {
|
|
|
57
61
|
} );
|
|
58
62
|
}, { once: true } );
|
|
59
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handles hash changes that happen without a full page reload, e.g. editing the
|
|
67
|
+
* hash in the address bar or navigating with the browser's back/forward buttons.
|
|
68
|
+
*
|
|
69
|
+
* In these cases the browser performs native fragment navigation, which scrolls
|
|
70
|
+
* the target to the very top of the viewport without accounting for the sticky
|
|
71
|
+
* header. Re-running the scroll with the proper offset keeps the heading visible.
|
|
72
|
+
*
|
|
73
|
+
* Clicking an `a[href^="#"]` link does not trigger this handler, because the click
|
|
74
|
+
* handler updates the URL via `history.pushState()`, which does not emit a
|
|
75
|
+
* `hashchange` event.
|
|
76
|
+
*
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
static _handleHashChange() {
|
|
80
|
+
window.addEventListener( 'hashchange', () => {
|
|
81
|
+
if ( !window.location.hash ) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
scrollToHash( window.location.hash.substring( 1 ), {
|
|
86
|
+
pushState: false
|
|
87
|
+
} );
|
|
88
|
+
} );
|
|
89
|
+
}
|
|
60
90
|
}
|
|
@@ -83,6 +83,11 @@ export class Header extends BaseComponent {
|
|
|
83
83
|
this.element.classList.toggle( 'is-on-top-overlay', state.isOnOverlay );
|
|
84
84
|
this.dispatchHeaderResizeEvent();
|
|
85
85
|
} else if ( prevState?.height !== state.height && state.height !== null ) {
|
|
86
|
+
// Expose the header height on the document root so that native fragment navigation
|
|
87
|
+
// (e.g. pasting a URL with a hash, which fires no `hashchange` or `load` event) can
|
|
88
|
+
// offset the target below the sticky header via `scroll-padding-top`. See `_base.scss`.
|
|
89
|
+
document.documentElement.style.setProperty( '--header-height', `${ state.height }px` );
|
|
90
|
+
|
|
86
91
|
this.dispatchHeaderResizeEvent();
|
|
87
92
|
}
|
|
88
93
|
}
|
|
@@ -128,7 +133,7 @@ export class Header extends BaseComponent {
|
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
/**
|
|
131
|
-
*
|
|
136
|
+
* Tracks the header height and updates the component state.
|
|
132
137
|
*/
|
|
133
138
|
_trackHeight() {
|
|
134
139
|
this.setState( {
|