umberto 8.0.3 → 8.2.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## [8.2.0](https://github.com/cksource/umberto/compare/v8.1.0...v8.2.0) (August 29, 2025)
5
+
6
+ ### Features
7
+
8
+ * Added integration with the [Kapa.ai](https://www.kapa.ai/) service.
9
+
10
+ Umberto configuration can now specify the `kapa` key that allows loading the Kapa button and widget.
11
+
12
+
13
+ ## [8.1.0](https://github.com/cksource/umberto/compare/v8.0.3...v8.1.0) (August 12, 2025)
14
+
15
+ ### Features
16
+
17
+ * Added support for relative paths in `packagesDir` option.
18
+
19
+ ### Bug fixes
20
+
21
+ * Updated how `<iframe>` elements are created to fix issues with automated tests using Chrome v139.
22
+
23
+
4
24
  ## [8.0.3](https://github.com/cksource/umberto/compare/v8.0.2...v8.0.3) (August 5, 2025)
5
25
 
6
26
  ### Bug fixes
@@ -33,34 +53,6 @@ Changelog
33
53
  * Fixed the project selection dropdown: after rebuilding, it now correctly displays all available projects instead of only one.
34
54
  * Optimized the performance of the navigation tree building process, resulting in faster page loads and smoother navigation.
35
55
 
36
-
37
- ## [8.0.0](https://github.com/cksource/umberto/compare/v7.0.2...v8.0.0) (July 18, 2025)
38
-
39
- ### BREAKING CHANGES
40
-
41
- * Updated the required version of Node.js to 22.
42
-
43
- ### Features
44
-
45
- * We have introduced a brand-new theme to elevate your UI experience. Enjoy a fresh look with improved aesthetics and feel.
46
-
47
- ### Bug fixes
48
-
49
- * Removed link to unnecessary stylesheet that results in a 404.
50
- * Fixed output path calculation on Windows environment for generated HTML files. Previously, non-normalized CWD was used which caused the generated output path to contain invalid characters and be incorrect.
51
-
52
- ### Other changes
53
-
54
- * The repository now uses ESLint v9. Therefore, the required Node.js version has been upgraded to 22 to match the ESLint requirements.
55
- * Update `LICENSE.md` file to include licenses of direct dependencies
56
-
57
-
58
- ## [7.0.2](https://github.com/cksource/umberto/compare/v7.0.1...v7.0.2) (2025-05-20)
59
-
60
- ### Bug fixes
61
-
62
- * Fixed an invalid protocol (should be `https://` instead of `https:/`) in the generated index sitemap file. Closes [#1277](https://github.com/cksource/umberto/issues/1277). ([commit](https://github.com/cksource/umberto/commit/6f59654e85b1230fbfe029efe68f708755ec6342))
63
-
64
56
  ---
65
57
 
66
58
  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": "8.0.3",
3
+ "version": "8.2.0",
4
4
  "description": "CKSource Documentation builder",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const applyDesignDocClasses = require( '../../../utils/apply-design-doc-classes' );
9
9
 
10
10
  /**
11
11
  * Global mapping of HTML elements to their corresponding design system classes.
@@ -86,60 +86,11 @@ hexo.extend.filter.register( 'after_post_render', page => {
86
86
  return page;
87
87
  }
88
88
 
89
- const $ = cheerio.load( page.content, null, false );
90
-
91
- // Apply classes based on the global mapping
92
- for ( const [ element, classesOrCallback ] of Object.entries( ELEMENTS_CLASSES_MAPPINGS ) ) {
93
- const $elements = $( element );
94
-
95
- $elements.each( ( _, element ) => {
96
- const $element = $( element );
97
-
98
- if ( hasClassOrParentWithClass( $element, 'no-transform' ) ) {
99
- return;
100
- }
101
-
102
- // Add the 'doc' class to each element that gets styled
103
- $element.addClass( 'doc' );
104
-
105
- const classList = ( $element.attr( 'class' )?.split( /\s+/ ) || [] ).filter(
106
- className => !ELEMENTS_WITH_WHITELIST_CLASSES.includes( className )
107
- );
108
-
109
- // Avoid applying classes to elements that already have one.
110
- if ( classList.length > 1 ) {
111
- return;
112
- }
113
-
114
- // Add the specified classes
115
- if ( typeof classesOrCallback === 'function' ) {
116
- const elementClasses = classesOrCallback( $element );
117
-
118
- if ( Array.isArray( elementClasses ) ) {
119
- $element.addClass( elementClasses.join( ' ' ) );
120
- } else if ( typeof elementClasses === 'string' ) {
121
- $element.addClass( elementClasses );
122
- }
123
- } else if ( Array.isArray( classesOrCallback ) ) {
124
- $element.addClass( classesOrCallback.join( ' ' ) );
125
- } else {
126
- $element.addClass( classesOrCallback );
127
- }
128
- } );
129
- }
130
-
131
- page.content = $.html();
89
+ page.content = applyDesignDocClasses( {
90
+ elementsClassesMappings: ELEMENTS_CLASSES_MAPPINGS,
91
+ elementsWithWhitelistClasses: ELEMENTS_WITH_WHITELIST_CLASSES,
92
+ content: page.content
93
+ } );
132
94
 
133
95
  return page;
134
96
  }, 40 );
135
-
136
- /**
137
- * Checks if the element itself or any of its parents has the specified class.
138
- *
139
- * @param $element - The jQuery element to check.
140
- * @param className - The class name to look for.
141
- * @returns True if the element or any parent has the class, false otherwise.
142
- */
143
- function hasClassOrParentWithClass( $element, className ) {
144
- return $element.hasClass( className ) || $element.parents( `.${ className }` ).length > 0;
145
- }
@@ -7,10 +7,7 @@
7
7
 
8
8
  const cheerio = require( 'cheerio' );
9
9
 
10
- /**
11
- * Enforce priority 30 to run it after apply design doc classes filter.
12
- */
13
- hexo.extend.filter.register( 'after_post_render', page => {
10
+ function wrapTableIntoWrappers( page ) {
14
11
  if ( page.projectTheme !== 'gloria' ) {
15
12
  return page;
16
13
  }
@@ -27,4 +24,13 @@ hexo.extend.filter.register( 'after_post_render', page => {
27
24
  page.content = $.html();
28
25
 
29
26
  return page;
30
- }, 30 );
27
+ };
28
+
29
+ /**
30
+ * Enforce priority 30 to run it after apply design doc classes filter.
31
+ */
32
+ if ( typeof hexo !== 'undefined' ) {
33
+ hexo.extend.filter.register( 'after_post_render', wrapTableIntoWrappers, 30 );
34
+ }
35
+
36
+ module.exports = wrapTableIntoWrappers;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const cheerio = require( 'cheerio' );
9
+
10
+ /**
11
+ * Add design system classes to the parsed document elements. If element has 0 CSS classes
12
+ * it'll be considered as not styled and the design system classes will be applied.
13
+
14
+ * @param options - Options for applying design classes.
15
+ * @param options.elementsClassesMappings - Mappings of elements to their classes or
16
+ * functions that return classes.
17
+ * @param options.elementsWithWhitelistClasses - List of classes that are
18
+ * considered as transparent in checks for amount of the classes.
19
+ * @param options.content - The HTML content to process.
20
+ */
21
+ module.exports = function applyDesignDocClasses(
22
+ {
23
+ elementsClassesMappings,
24
+ elementsWithWhitelistClasses,
25
+ content
26
+ }
27
+ ) {
28
+ const $ = cheerio.load( content, null, false );
29
+
30
+ // Apply classes based on the global mapping
31
+ for ( const [ element, classesOrCallback ] of Object.entries( elementsClassesMappings ) ) {
32
+ const $elements = $( element );
33
+
34
+ $elements.each( ( _, element ) => {
35
+ const $element = $( element );
36
+
37
+ if ( hasClassOrParentWithClass( $element, 'no-transform' ) ) {
38
+ return;
39
+ }
40
+
41
+ // Add the 'doc' class to each element that gets styled
42
+ $element.addClass( 'doc' );
43
+
44
+ const classList = ( $element.attr( 'class' )?.split( /\s+/ ) || [] ).filter(
45
+ className => !elementsWithWhitelistClasses.includes( className )
46
+ );
47
+
48
+ // Avoid applying classes to elements that already have one.
49
+ if ( classList.length > 1 ) {
50
+ return;
51
+ }
52
+
53
+ // Add the specified classes
54
+ if ( typeof classesOrCallback === 'function' ) {
55
+ const elementClasses = classesOrCallback( $element );
56
+
57
+ if ( Array.isArray( elementClasses ) ) {
58
+ $element.addClass( elementClasses.join( ' ' ) );
59
+ } else if ( typeof elementClasses === 'string' ) {
60
+ $element.addClass( elementClasses );
61
+ }
62
+ } else if ( Array.isArray( classesOrCallback ) ) {
63
+ $element.addClass( classesOrCallback.join( ' ' ) );
64
+ } else {
65
+ $element.addClass( classesOrCallback );
66
+ }
67
+ } );
68
+ }
69
+
70
+ return $.html();
71
+ };
72
+
73
+ /**
74
+ * Checks if the element itself or any of its parents has the specified class.
75
+ *
76
+ * @param $element - The jQuery element to check.
77
+ * @param className - The class name to look for.
78
+ * @returns True if the element or any parent has the class, false otherwise.
79
+ */
80
+ function hasClassOrParentWithClass( $element, className ) {
81
+ return $element.hasClass( className ) || $element.parents( `.${ className }` ).length > 0;
82
+ }
@@ -12,6 +12,6 @@
12
12
  * @param {*} value - The initial value to be processed by the functions.
13
13
  * @returns {*} The final result after all functions have been applied.
14
14
  */
15
- module.exports = function pipe( ...fns ) {
15
+ module.exports = function compose( ...fns ) {
16
16
  return value => fns.reduce( ( acc, fn ) => fn( acc ), value );
17
17
  };
@@ -7,13 +7,13 @@
7
7
 
8
8
  const dropInitSlash = require( './drop-init-slash' );
9
9
  const dropTrailingSlash = require( './drop-trailing-slash' );
10
- const pipe = require( './pipe' );
10
+ const compose = require( './compose' );
11
11
 
12
12
  module.exports = function concatUrlParts( ...urls ) {
13
13
  return (
14
14
  urls
15
15
  .filter( Boolean )
16
- .map( pipe( dropTrailingSlash, dropInitSlash ) )
16
+ .map( compose( dropTrailingSlash, dropInitSlash ) )
17
17
  .join( '/' )
18
18
  );
19
19
  };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ /**
9
+ * Wrapper around `require.resolve()` to allow mocking it in tests.
10
+ */
11
+ module.exports = function resolvePath( path, options ) {
12
+ return require.resolve( path, options );
13
+ };
@@ -20,6 +20,7 @@ const umbertoVersion = require( '../../../package.json' ).version;
20
20
  * @param {Object} googleoptimize Google Optimize config.
21
21
  * @param {Object} googletagmanager Google Tag Manager config.
22
22
  * @param {Object} googleanalytics Google Analytics config.
23
+ * @param {Object} kapa Kapa.ai config.
23
24
  * @param {Object} vwo Visual Website Optimizer config.
24
25
  * @param {Object} feedbackWidget Feedback widget config
25
26
  * @param {Array} extraStylePaths Paths to extra external css.
@@ -36,6 +37,7 @@ module.exports = ( ctx, {
36
37
  googleoptimize,
37
38
  googletagmanager,
38
39
  googleanalytics,
40
+ kapa,
39
41
  promobar,
40
42
  vwo,
41
43
  feedbackWidget,
@@ -105,6 +107,7 @@ module.exports = ( ctx, {
105
107
  locals.googleoptimize = googleoptimize;
106
108
  locals.googletagmanager = googletagmanager;
107
109
  locals.googleanalytics = googleanalytics;
110
+ locals.kapa = kapa;
108
111
  locals.promobar = promobar;
109
112
  locals.vwo = vwo;
110
113
  locals.feedbackWidget = feedbackWidget;
@@ -17,7 +17,7 @@ module.exports = ( {
17
17
  guidesToRepos.default = defaultRepoUrl;
18
18
 
19
19
  for ( const item of packages ) {
20
- const packageRepoUrl = getRepoUrl( upath.join( rootPath, item.path ) );
20
+ const packageRepoUrl = getRepoUrl( upath.resolve( rootPath, item.path ) );
21
21
  globSync( upath.join( rootPath, item.path, 'docs', '**', '*.md' ) )
22
22
  .map( p => upath.normalize( p ) )
23
23
  .map( p => p.replace( upath.join( rootPath, item.path, 'docs' ), '' ) )
@@ -141,6 +141,7 @@ module.exports = options => {
141
141
  googleoptimize: mainConfig.googleoptimize,
142
142
  googletagmanager: mainConfig.googletagmanager,
143
143
  googleanalytics: mainConfig.googleanalytics,
144
+ kapa: mainConfig.kapa,
144
145
  promobar: mainConfig.promobar,
145
146
  sitemap: mainConfig.sitemap,
146
147
  vwo: mainConfig.vwo,
@@ -275,6 +276,7 @@ async function buildProjects( rootPath, projectPaths, options = {} ) {
275
276
  googleoptimize: options.googleoptimize,
276
277
  googletagmanager: options.googletagmanager,
277
278
  googleanalytics: options.googleanalytics,
279
+ kapa: options.kapa,
278
280
  promobar: options.promobar,
279
281
  sitemap: options.sitemap,
280
282
  vwo: options.vwo,
@@ -353,6 +355,7 @@ async function buildProjects( rootPath, projectPaths, options = {} ) {
353
355
  googleoptimize: options.googleoptimize,
354
356
  googletagmanager: options.googletagmanager,
355
357
  googleanalytics: options.googleanalytics,
358
+ kapa: options.kapa,
356
359
  promobar: options.promobar,
357
360
  quickNavigationProjects: options.quickNavigationProjects,
358
361
  vwo: options.vwo,
@@ -465,6 +468,7 @@ async function buildApis( projectConfigs, options = {} ) {
465
468
  googleoptimize: options.googleoptimize,
466
469
  googletagmanager: options.googletagmanager,
467
470
  googleanalytics: options.googleanalytics,
471
+ kapa: options.kapa,
468
472
  promobar: options.promobar,
469
473
  vwo: options.vwo,
470
474
  feedbackWidget: options.feedbackWidget,
@@ -58,13 +58,13 @@ module.exports = ( rootPath, config, options ) => {
58
58
  if ( config.items && Array.isArray( config.items ) ) {
59
59
  for ( const item of config.items ) {
60
60
  hexoManager.addOriginPath(
61
- upath.join( rootPath, item.path, config.path ),
62
- upath.join( hexoManager.getSourceDir(), BASE_PATH )
61
+ upath.resolve( rootPath, item.path, config.path ),
62
+ upath.resolve( hexoManager.getSourceDir(), BASE_PATH )
63
63
  );
64
64
 
65
65
  promises.push( copyDocFiles(
66
- upath.join( rootPath, item.path, config.path ),
67
- upath.join( hexoManager.getSourceDir(), BASE_PATH ),
66
+ upath.resolve( rootPath, item.path, config.path ),
67
+ upath.resolve( hexoManager.getSourceDir(), BASE_PATH ),
68
68
  {
69
69
  match,
70
70
  allOutputPaths,
@@ -7,6 +7,7 @@
7
7
 
8
8
  const upath = require( 'upath' );
9
9
  const fs = require( 'fs-extra' );
10
+ const resolvePath = require( '../helpers/resolve-path' );
10
11
 
11
12
  /**
12
13
  * @returns {Promise<void>}
@@ -19,7 +20,9 @@ module.exports = async function copyProjectIcons( projectGlobals, buildDirectory
19
20
  const destinationIconsPath = upath.join( buildDirectory, projectDetails.BASE_PATH, 'assets', 'icons' );
20
21
 
21
22
  for ( const [ sourceFile, destinationName ] of projectDetails._icons ) {
22
- const fullSourcePath = upath.join( projectDetails.config.projectRootPath, 'node_modules', sourceFile );
23
+ const fullSourcePath = resolvePath( sourceFile, {
24
+ paths: [ projectDetails.config.projectRootPath ]
25
+ } );
23
26
  const fullDestinationPath = upath.join( destinationIconsPath, destinationName );
24
27
 
25
28
  try {
@@ -16,12 +16,13 @@
16
16
  //- +button({ variant: 'secondary' })
17
17
  //- +button({ variant: 'transparent', fillColor: '--color-orange-600' })
18
18
  //- span Custom content
19
- mixin button({ variant = 'primary', tooltip, blockDisplay, active, disabled, id, className, square, rounded, type = 'button', size = 'base', label, ariaLabel, ariaControls, icon, labelClassName, iconClassName, fillColor, iconPosition = 'left' })
19
+ mixin button({ variant = 'primary', tooltip, blockDisplay, active, disabled, id, className, square, rounded, type = 'button', size = 'base', label, ariaLabel, ariaControls, icon, labelClassName, iconClassName, fillColor, iconPosition = 'left', onClick })
20
20
  - const showTooltip = !!id && !!tooltip && !ariaLabel;
21
21
 
22
22
  button.c-button(
23
23
  id=id,
24
24
  disabled=disabled,
25
+ onClick=onClick || false,
25
26
  class={
26
27
  'is-block': blockDisplay,
27
28
  'is-active': active,
@@ -45,6 +45,8 @@ if page.BASE_PATH && page.BASE_PATH !== '.' && !page.BASE_PATH.includes( 'latest
45
45
  link( rel = 'canonical' href = page.canonicalUrlBeginning + page.path.replace( page.BASE_PATH, projectLocals.latestBasePath ) )
46
46
 
47
47
  +head-preloads()
48
+ if kapa
49
+ +load-kapa-script(kapa)
48
50
 
49
51
  script(src=relative_url(page.path, pathJoin( 'assets', umbertoVersion, 'gloria/scripts/app.js')))
50
52
 
@@ -50,6 +50,8 @@ mixin header({ withChooseProject = true, className = '' } = {})
50
50
 
51
51
  .c-header__brand-row-toolbar.u-flex-horizontal.u-align-items-center
52
52
  +algolia-search
53
+ if kapa
54
+ +kapa-button
53
55
  +button-link({
54
56
  label: 'Sign up',
55
57
  icon: 'export',
@@ -12,3 +12,4 @@ include ./header-promobar/index
12
12
  include ./breadcrumbs/index
13
13
  include ./algolia-search/index
14
14
  include ./not-found/index
15
+ include ./kapa/index
@@ -0,0 +1,102 @@
1
+ mixin load-kapa-script(kapa)
2
+ -
3
+ const mainColor = '#743CCD';
4
+ const textPrimaryColor = '#00091B';
5
+ const textSecondaryColor = '#585B5F';
6
+ const infoPanelBackgroundColor = '#F8F9FA';
7
+ const standardBorder = '1px solid var(--mantine-color-gray-3)';
8
+ const buttonHoverBgColor = 'var(--mantine-color-default-hover)';
9
+ const exampleQuestionsTitleColor = 'var(--mantine-color-gray-text)';
10
+
11
+ var attributes = {
12
+ 'async': true,
13
+ 'fetchpriority': 'low',
14
+ 'src': 'https://widget.kapa.ai/kapa-widget.bundle.js',
15
+ 'data-website-id': kapa.widgetOptions.websiteId,
16
+ 'data-project-name': kapa.widgetOptions.projectName,
17
+
18
+ // Global settings.
19
+ 'data-project-color': mainColor,
20
+ 'data-project-logo': 'https://ckeditor.com/docs/ckeditor5/latest/assets/img/ckeditor5-logo.svg',
21
+ 'data-font-family': '"Title Mullish", text public sans',
22
+
23
+ // Floating button.
24
+ 'data-button-text': 'Need help?',
25
+ 'data-button-image': 'https://ckeditor.com/docs/ckeditor5/latest/assets/img/ckeditor5-logo-white.svg',
26
+ 'data-button-width': 'fit-content',
27
+ 'data-button-height': '90px',
28
+ 'data-button-padding': '12px',
29
+
30
+ // Modal style.
31
+ 'data-modal-size': '900px',
32
+ 'data-modal-z-index': '2000',
33
+ 'data-modal-border-radius': '6px',
34
+
35
+ // Modal title.
36
+ 'data-modal-title': 'AI Helper',
37
+ 'data-modal-title-color': textPrimaryColor,
38
+
39
+ // Modal disclaimer.
40
+ 'data-modal-disclaimer': 'Disclaimer: Responses are generated by AI and may contain inaccuracies. Please verify the information with the official documentation.',
41
+ 'data-modal-disclaimer-text-color': exampleQuestionsTitleColor,
42
+ 'data-modal-disclaimer-bg-color': infoPanelBackgroundColor,
43
+
44
+ // Modal example questions.
45
+ 'data-example-question-button-border': standardBorder,
46
+ 'data-example-question-button-text-color': textSecondaryColor,
47
+
48
+ // Modal question input.
49
+ 'data-query-input-text-color': textPrimaryColor,
50
+ 'data-query-input-placeholder-text-color': textSecondaryColor,
51
+ 'data-modal-ask-ai-input-placeholder': 'Ask me a question about the project...',
52
+
53
+ // Modal deep thinking button.
54
+ 'data-deep-thinking-button-text-color': textSecondaryColor,
55
+
56
+ // Modal answer.
57
+ 'data-question-text-color': textPrimaryColor,
58
+ 'data-answer-text-color': textPrimaryColor,
59
+
60
+ // Modal copy, clear & feedback buttons.
61
+ 'data-answer-copy-button-bg-color': 'white',
62
+ 'data-answer-copy-button-border': standardBorder,
63
+ 'data-answer-copy-button-hover-bg-color': buttonHoverBgColor,
64
+ 'data-thread-clear-button-bg-color': 'white',
65
+ 'data-thread-clear-button-border': standardBorder,
66
+ 'data-thread-clear-button-hover-bg-color': buttonHoverBgColor,
67
+ 'data-answer-feedback-button-bg-color': 'white',
68
+ 'data-answer-feedback-button-border': standardBorder,
69
+ 'data-answer-feedback-button-active-bg-color': buttonHoverBgColor,
70
+
71
+ // Modal answer source buttons.
72
+ 'data-source-link-primary-heading-text-color': textPrimaryColor,
73
+ 'data-source-link-secondary-heading-text-color': textSecondaryColor,
74
+ 'data-source-link-hover-bg-color': buttonHoverBgColor,
75
+ }
76
+
77
+ const projectConfig = projectLocals && kapa[ projectLocals.projectSlug ];
78
+ const modalTitle = projectConfig && projectConfig.modalTitle;
79
+ const questionPlaceholder = projectConfig && projectConfig.questionPlaceholder;
80
+ const exampleQuestions = projectConfig && projectConfig.exampleQuestions;
81
+
82
+ if ( modalTitle ) {
83
+ attributes[ 'data-modal-title' ] = modalTitle;
84
+ }
85
+
86
+ if ( questionPlaceholder ) {
87
+ attributes[ 'data-modal-ask-ai-input-placeholder' ] = questionPlaceholder;
88
+ }
89
+
90
+ if ( exampleQuestions && exampleQuestions.length ) {
91
+ attributes[ 'data-modal-example-questions' ] = exampleQuestions.join( ',' );
92
+ }
93
+
94
+ script&attributes(attributes)
95
+
96
+ mixin kapa-button
97
+ +button({
98
+ label: 'Ask AI',
99
+ icon: 'chat',
100
+ variant: 'toolbar',
101
+ onClick: 'window.Kapa(\'open\')'
102
+ })
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M1.76782 15.2671C1.82839 15.2671 1.88757 15.2565 1.94458 15.2388L4.21411 14.4995C6.12656 15.1316 8.2129 14.7834 9.80396 13.6245L9.79517 13.6167C10.4093 13.5828 11.0272 13.4619 11.6252 13.2495L13.886 13.9614C13.9575 13.984 14.033 13.9924 14.1077 13.9858C14.1823 13.9793 14.2551 13.958 14.3215 13.9233C14.388 13.8887 14.4472 13.8411 14.4954 13.7837C14.5434 13.7263 14.5794 13.6598 14.6018 13.5884C14.6197 13.5348 14.6292 13.4776 14.6292 13.4204L14.6428 11.0522C16.3963 8.78261 16.4699 5.58834 14.7502 3.21338C12.6163 0.290177 8.51232 -0.357907 5.58911 1.76709C5.20354 2.0456 4.85922 2.35857 4.55396 2.69775L4.55396 2.6958C3.19644 3.06057 2.00514 3.87984 1.17896 5.01709C-0.417359 7.21518 -0.376417 10.1666 1.19653 12.3022L1.19653 14.6958C1.19668 15.0116 1.45198 15.2669 1.76782 15.2671ZM11.5891 11.8921L11.3752 11.981C9.15207 12.847 6.59175 12.1221 5.16138 10.1597C3.45604 7.81327 3.97848 4.51834 6.33911 2.80225C8.69255 1.09877 11.9926 1.62058 13.7141 3.98096C15.1284 5.92914 15.0285 8.58439 13.5178 10.4272L13.3752 10.5884L13.3577 12.4458L11.5891 11.8921ZM2.50024 11.856L2.35767 11.6772C0.991728 9.98443 0.917806 7.56318 2.21411 5.78467C2.53375 5.34003 2.91082 4.97189 3.32153 4.67725L3.33228 4.67041C2.53946 6.68459 2.76114 9.04694 4.12524 10.9272C4.96108 12.0747 6.14695 12.9202 7.50415 13.3364L7.50024 13.3384C6.51275 13.6152 5.44653 13.5848 4.44653 13.2134L4.23267 13.1245L2.48267 13.7134L2.50024 11.856ZM6.9104 7.8042C7.3229 7.8042 7.64282 7.484 7.64282 7.08936C7.6426 6.69491 7.32276 6.37549 6.9104 6.37549C6.53378 6.37553 6.21434 6.69493 6.21411 7.08936C6.21411 7.48397 6.53365 7.80415 6.9104 7.8042ZM9.4104 7.8042C9.8229 7.8042 10.1428 7.484 10.1428 7.08936C10.1426 6.69491 9.82276 6.37549 9.4104 6.37549C9.03378 6.37553 8.71434 6.69493 8.71411 7.08936C8.71411 7.48397 9.03365 7.80415 9.4104 7.8042ZM11.9104 7.8042C12.3229 7.8042 12.6428 7.484 12.6428 7.08936C12.6426 6.69491 12.3228 6.37549 11.9104 6.37549C11.5338 6.37554 11.2143 6.69493 11.2141 7.08936C11.2141 7.48397 11.5337 7.80415 11.9104 7.8042Z" fill="#585B5F"/>
3
+ </svg>
@@ -71,10 +71,7 @@ export class IFrame extends BaseComponent {
71
71
  * @param content - HTML content to load
72
72
  */
73
73
  setContent( content ) {
74
- const iframe = this.elements.iframe;
75
- const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
76
-
77
- const wrappedHTML = `
74
+ this.elements.iframe.srcdoc = `
78
75
  <!DOCTYPE html>
79
76
  ${ content }
80
77
  ${ this.config.autoResize ? `
@@ -91,12 +88,6 @@ export class IFrame extends BaseComponent {
91
88
  </script>
92
89
  ` : '' }
93
90
  `;
94
-
95
- if ( iframeDocument ) {
96
- iframeDocument.open();
97
- iframeDocument.write( wrappedHTML );
98
- iframeDocument.close();
99
- }
100
91
  }
101
92
 
102
93
  /**
@@ -3,6 +3,6 @@
3
3
  * For licensing, see LICENSE.md.
4
4
  */
5
5
 
6
- export function pipe( ...fns ) {
6
+ export function compose( ...fns ) {
7
7
  return value => fns.reduce( ( acc, fn ) => fn( acc ), value );
8
8
  }
@@ -6,7 +6,7 @@
6
6
  import { BaseComponent } from '../components/base-component';
7
7
  import { identity } from '../helpers/identity';
8
8
  import { injectScript } from '../helpers/inject-script';
9
- import { pipe } from '../helpers/pipe';
9
+ import { compose } from '../helpers/compose';
10
10
 
11
11
  const DOCSEARCH_JS_URL = 'https://cdn.jsdelivr.net/npm/@docsearch/js@3.9.0/dist/umd/index.min.js';
12
12
 
@@ -67,7 +67,7 @@ export class AlgoliaSearch extends BaseComponent {
67
67
 
68
68
  client.search = async ( options, ...rest ) => {
69
69
  const { results: [ firstResult ] } = await originalSearch( options, ...rest );
70
- const mappedResults = pipe(
70
+ const mappedResults = compose(
71
71
  filterByTags( { slug, tags } ),
72
72
  boostByTags( boost.tags )
73
73
  )( firstResult.hits );
@@ -110,7 +110,7 @@ function boostByTags( tags ) {
110
110
  return identity;
111
111
  }
112
112
 
113
- return pipe(
113
+ return compose(
114
114
  groupByTag,
115
115
  sortObjectKeysByIndices( tags ),
116
116
  Object.values,