├── packages
├── kg-simplemde
│ ├── .nvmrc
│ ├── yarn.lock
│ ├── .gitignore
│ ├── .eslintrc
│ └── bower.json
├── kg-converters
│ ├── .eslintignore
│ ├── .gitignore
│ ├── index.js
│ ├── lib
│ │ └── kg-converters.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── exports.test.js
│ ├── .eslintrc.js
│ └── README.md
├── kg-clean-basic-html
│ ├── .eslintignore
│ ├── .gitignore
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ ├── .eslintrc.js
│ └── README.md
├── kg-default-nodes
│ ├── .eslintignore
│ ├── index.js
│ ├── .gitignore
│ ├── lib
│ │ ├── utils
│ │ │ ├── is-unsplash-image.js
│ │ │ ├── is-local-content-image.js
│ │ │ ├── escape-html.js
│ │ │ ├── slugify.js
│ │ │ ├── clean-dom.js
│ │ │ ├── build-clean-basic-html-for-element.js
│ │ │ ├── read-caption-from-element.js
│ │ │ ├── render-empty-container.js
│ │ │ ├── get-resized-image-dimensions.js
│ │ │ ├── tagged-template-fns.mjs
│ │ │ ├── rgb-to-hex.js
│ │ │ ├── size-byte-converter.js
│ │ │ └── add-create-document-option.js
│ │ ├── nodes
│ │ │ ├── at-link
│ │ │ │ ├── index.js
│ │ │ │ └── kg-link.svg
│ │ │ ├── horizontalrule
│ │ │ │ ├── horizontalrule-parser.js
│ │ │ │ ├── horizontalrule-renderer.js
│ │ │ │ └── HorizontalRuleNode.js
│ │ │ ├── paywall
│ │ │ │ ├── paywall-renderer.js
│ │ │ │ ├── paywall-parser.js
│ │ │ │ └── PaywallNode.js
│ │ │ ├── email
│ │ │ │ └── EmailNode.js
│ │ │ ├── markdown
│ │ │ │ ├── markdown-renderer.js
│ │ │ │ └── MarkdownNode.js
│ │ │ ├── aside
│ │ │ │ └── AsideParser.js
│ │ │ ├── html
│ │ │ │ └── HtmlNode.js
│ │ │ ├── toggle
│ │ │ │ └── ToggleNode.js
│ │ │ ├── button
│ │ │ │ └── ButtonNode.js
│ │ │ └── audio
│ │ │ │ └── AudioNode.js
│ │ ├── KoenigDecoratorNode.js
│ │ └── serializers
│ │ │ └── paragraph.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ ├── test-utils
│ │ │ ├── overrides.js
│ │ │ └── assertions.js
│ │ └── utils
│ │ │ └── rgb-to-hex.test.js
│ └── .eslintrc.js
├── kg-parser-plugins
│ ├── .eslintignore
│ ├── .gitignore
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ ├── lib
│ │ └── cards
│ │ │ └── soft-return.js
│ └── .eslintrc.js
├── kg-html-to-lexical
│ ├── .gitignore
│ ├── src
│ │ ├── index.ts
│ │ └── decs.d.ts
│ ├── .eslintrc.js
│ ├── test
│ │ └── .eslintrc.js
│ └── README.md
├── kg-default-transforms
│ ├── .gitignore
│ ├── src
│ │ ├── index.ts
│ │ ├── typings.d.ts
│ │ ├── kg-default-nodes.d.ts
│ │ └── transforms
│ │ │ ├── remove-alignment.ts
│ │ │ └── merge-list-nodes.ts
│ ├── .eslintrc.js
│ ├── test
│ │ └── .eslintrc.js
│ └── README.md
├── kg-utils
│ ├── index.js
│ ├── lib
│ │ └── kg-utils.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ ├── README.md
│ └── package.json
├── kg-default-atoms
│ ├── index.js
│ ├── lib
│ │ └── atoms
│ │ │ ├── index.js
│ │ │ └── soft-return.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ ├── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ │ └── atoms
│ │ │ └── soft-return.test.js
│ ├── README.md
│ └── package.json
├── kg-default-cards
│ ├── index.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ ├── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ │ ├── cards
│ │ │ ├── hr.test.js
│ │ │ └── paywall.test.js
│ │ └── lib
│ │ │ └── utils
│ │ │ └── is-unsplash-image.test.js
│ ├── lib
│ │ ├── utils
│ │ │ ├── is-unsplash-image.js
│ │ │ ├── dedent.js
│ │ │ ├── is-local-content-image.js
│ │ │ ├── hbs.js
│ │ │ ├── index.js
│ │ │ └── resize-image.js
│ │ └── cards
│ │ │ ├── hr.js
│ │ │ ├── paywall.js
│ │ │ └── index.js
│ └── README.md
├── kg-lexical-html-renderer
│ ├── .gitignore
│ ├── lib
│ │ ├── index.ts
│ │ ├── kg-utils.d.ts
│ │ ├── transformers
│ │ │ ├── element
│ │ │ │ ├── paragraph.ts
│ │ │ │ ├── heading.ts
│ │ │ │ ├── aside.ts
│ │ │ │ └── blockquote.ts
│ │ │ └── index.ts
│ │ ├── utils
│ │ │ └── generate-id.ts
│ │ └── get-dynamic-data-nodes.ts
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── assertions.js
│ │ │ ├── index.js
│ │ │ ├── overrides.js
│ │ │ └── should-render.js
│ └── .eslintrc.js
├── koenig-lexical
│ ├── .lintstagedrc
│ ├── src
│ │ ├── utils
│ │ │ ├── isGif.js
│ │ │ ├── ctrlOrCmd.js
│ │ │ ├── constants.js
│ │ │ ├── getImageFilenameFromSrc.js
│ │ │ ├── openFileSelection.js
│ │ │ ├── index.js
│ │ │ ├── storybook
│ │ │ │ └── populate-storybook-editor.js
│ │ │ ├── getAccentColor.js
│ │ │ ├── getTopLevelNativeElement.js
│ │ │ ├── $selectDecoratorNode.js
│ │ │ ├── shortcutSymbols.js
│ │ │ ├── prettifyFileName.js
│ │ │ ├── analytics.js
│ │ │ ├── getEditorCardNodes.js
│ │ │ ├── isInternalUrl.js
│ │ │ ├── autoExpandTextArea.js
│ │ │ ├── getAudioMetadata.js
│ │ │ ├── getImageDimensions.js
│ │ │ ├── getScrollParent.js
│ │ │ ├── callToActionColors.js
│ │ │ ├── isEditorEmpty.js
│ │ │ ├── getSelectedNode.js
│ │ │ ├── thumbnailUploadHandler.js
│ │ │ ├── dataSrcToFile.js
│ │ │ ├── shouldIgnoreEvent.js
│ │ │ ├── getDOMRangeRect.js
│ │ │ ├── sanitize-html.js
│ │ │ └── $insertAndSelectNode.js
│ │ ├── context
│ │ │ ├── CardContext.jsx
│ │ │ ├── KoenigComposerContext.jsx
│ │ │ ├── SharedOnChangeContext.jsx
│ │ │ └── SharedHistoryContext.jsx
│ │ ├── components
│ │ │ ├── ui
│ │ │ │ ├── SlashMenu.jsx
│ │ │ │ ├── cards
│ │ │ │ │ ├── HorizontalRuleCard.jsx
│ │ │ │ │ └── PaywallCard.jsx
│ │ │ │ ├── ReadOnlyOverlay.jsx
│ │ │ │ ├── EditorPlaceholder.jsx
│ │ │ │ ├── TextInput.jsx
│ │ │ │ ├── CardVisibilityMessage.jsx
│ │ │ │ ├── FileUploadForm.jsx
│ │ │ │ ├── AudioUploadForm.jsx
│ │ │ │ ├── Input.stories.jsx
│ │ │ │ ├── Toggle.stories.jsx
│ │ │ │ ├── MediaPlayer.stories.jsx
│ │ │ │ ├── ActionToolbar.jsx
│ │ │ │ ├── PlusMenu.stories.jsx
│ │ │ │ ├── Slider.stories.jsx
│ │ │ │ ├── SubscribeForm.stories.jsx
│ │ │ │ ├── file-selectors
│ │ │ │ │ ├── UnsplashModal.jsx
│ │ │ │ │ └── Tenor
│ │ │ │ │ │ ├── Error.jsx
│ │ │ │ │ │ └── Loader.jsx
│ │ │ │ ├── ProgressBar.stories.jsx
│ │ │ │ ├── Delayed.jsx
│ │ │ │ ├── IconButton.stories.jsx
│ │ │ │ ├── ProgressBar.jsx
│ │ │ │ ├── Tooltip.jsx
│ │ │ │ ├── LinkToolbar.stories.jsx
│ │ │ │ ├── ImageUploadForm.jsx
│ │ │ │ ├── LinkInput.stories.jsx
│ │ │ │ ├── IconButton.jsx
│ │ │ │ ├── LinkInputWithSearch.stories.jsx
│ │ │ │ ├── HighlightedString.jsx
│ │ │ │ ├── EmojiPicker.jsx
│ │ │ │ ├── ImageUploadSwatch.jsx
│ │ │ │ ├── ColorPicker.stories.jsx
│ │ │ │ └── Dropdown.stories.jsx
│ │ │ ├── KoenigErrorBoundary.jsx
│ │ │ └── KoenigEditor.jsx
│ │ ├── assets
│ │ │ └── icons
│ │ │ │ ├── kg-add.svg
│ │ │ │ ├── kg-close.svg
│ │ │ │ ├── kg-align-center.svg
│ │ │ │ ├── kg-align-left.svg
│ │ │ │ ├── kg-card-type-unsplash.svg
│ │ │ │ ├── kg-heading-2.svg
│ │ │ │ ├── kg-card-type-divider.svg
│ │ │ │ ├── kg-expand.svg
│ │ │ │ ├── kg-heading-3.svg
│ │ │ │ ├── kg-italic.svg
│ │ │ │ ├── kg-play.svg
│ │ │ │ ├── kg-unsplash-heart.svg
│ │ │ │ ├── kg-card-type-html.svg
│ │ │ │ ├── kg-arrow-top-right.svg
│ │ │ │ ├── kg-card-type-email-cta.svg
│ │ │ │ ├── kg-download.svg
│ │ │ │ ├── kg-indicator-html.svg
│ │ │ │ ├── plus.svg
│ │ │ │ ├── kg-bold.svg
│ │ │ │ ├── kg-card-type-bookmark.svg
│ │ │ │ ├── kg-quote.svg
│ │ │ │ ├── kg-card-type-collection.svg
│ │ │ │ ├── kg-arrow-down.svg
│ │ │ │ ├── kg-card-type-x.svg
│ │ │ │ ├── kg-card-type-button.svg
│ │ │ │ ├── kg-quote-1.svg
│ │ │ │ ├── kg-card-type-header.svg
│ │ │ │ ├── kg-edit.svg
│ │ │ │ ├── kg-file-placeholder.svg
│ │ │ │ ├── kg-snippet.svg
│ │ │ │ ├── kg-unmute.svg
│ │ │ │ ├── kg-card-type-gif.svg
│ │ │ │ ├── kg-search.svg
│ │ │ │ ├── kg-card-type-snippet.svg
│ │ │ │ ├── kg-card-type-video.svg
│ │ │ │ ├── kg-card-type-product.svg
│ │ │ │ ├── kg-img-wide.svg
│ │ │ │ ├── kg-img-regular.svg
│ │ │ │ ├── kg-card-type-other.svg
│ │ │ │ ├── kg-layout-split.svg
│ │ │ │ ├── kg-eyedropper.svg
│ │ │ │ ├── kg-indicator-markdown.svg
│ │ │ │ ├── kg-img-full.svg
│ │ │ │ ├── kg-shrink.svg
│ │ │ │ ├── kg-toggle-arrow.svg
│ │ │ │ ├── kg-star.svg
│ │ │ │ ├── kg-layout-immersive.svg
│ │ │ │ ├── kg-gallery-placeholder.svg
│ │ │ │ ├── kg-card-type-file.svg
│ │ │ │ ├── kg-file-upload.svg
│ │ │ │ ├── kg-product-placeholder.svg
│ │ │ │ ├── kg-quote-2.svg
│ │ │ │ ├── kg-card-type-audio.svg
│ │ │ │ ├── kg-card-type-markdown.svg
│ │ │ │ ├── kg-img-placeholder.svg
│ │ │ │ ├── kg-card-type-signup.svg
│ │ │ │ ├── kg-img-bg.svg
│ │ │ │ ├── kg-replace.svg
│ │ │ │ ├── kg-card-type-preview.svg
│ │ │ │ ├── kg-card-type-toggle.svg
│ │ │ │ ├── kg-layout-list.svg
│ │ │ │ ├── kg-swap.svg
│ │ │ │ ├── kg-video-placeholder.svg
│ │ │ │ ├── kg-layout-minimal.svg
│ │ │ │ ├── kg-link.svg
│ │ │ │ ├── kg-card-type-callout.svg
│ │ │ │ ├── kg-card-type-gen-embed.svg
│ │ │ │ ├── kg-wand.svg
│ │ │ │ ├── kg-eye.svg
│ │ │ │ ├── kg-trash.svg
│ │ │ │ ├── kg-sync.svg
│ │ │ │ ├── kg-audio-file.svg
│ │ │ │ ├── kg-card-type-email.svg
│ │ │ │ ├── kg-audio-placeholder.svg
│ │ │ │ ├── kg-indicator-email.svg
│ │ │ │ ├── kg-layout-grid.svg
│ │ │ │ ├── kg-card-type-youtube.svg
│ │ │ │ ├── kg-eye-closed.svg
│ │ │ │ ├── kg-card-type-gallery.svg
│ │ │ │ ├── kg-upload-fill.svg
│ │ │ │ ├── kg-card-type-twitter.svg
│ │ │ │ ├── kg-card-type-image.svg
│ │ │ │ ├── kg-help.svg
│ │ │ │ └── kg-card-type-spotify.svg
│ │ ├── nodes
│ │ │ ├── MinimalNodes.js
│ │ │ └── BasicNodes.js
│ │ ├── plugins
│ │ │ ├── TKCountPlugin.jsx
│ │ │ ├── CardMenuPlugin.jsx
│ │ │ ├── KoenigBlurPlugin.jsx
│ │ │ └── KoenigFocusPlugin.jsx
│ │ ├── styles
│ │ │ └── components
│ │ │ │ └── react-colorful.css
│ │ └── hooks
│ │ │ ├── useClickOutside.js
│ │ │ └── usePreviousFocus.js
│ ├── test
│ │ ├── utils
│ │ │ └── isTestEnv.js
│ │ ├── test-setup.js
│ │ ├── e2e
│ │ │ ├── fixtures
│ │ │ │ ├── video.mp4
│ │ │ │ ├── print-img.pdf
│ │ │ │ ├── video-fail.mp4
│ │ │ │ ├── audio-sample.mp3
│ │ │ │ ├── large-image.jpeg
│ │ │ │ ├── large-image.png
│ │ │ │ ├── large-image-0.png
│ │ │ │ ├── large-image-1.png
│ │ │ │ ├── large-image-2.png
│ │ │ │ ├── large-image-3.png
│ │ │ │ ├── large-image-4.png
│ │ │ │ ├── large-image-5.png
│ │ │ │ ├── large-image-6.png
│ │ │ │ ├── large-image-7.png
│ │ │ │ ├── large-image-8.png
│ │ │ │ ├── large-image-9.png
│ │ │ │ ├── audio-sample-fail.mp3
│ │ │ │ └── large-image-fail.jpeg
│ │ │ └── plugins
│ │ │ │ └── assets
│ │ │ │ ├── large.jpeg
│ │ │ │ └── large.png
│ │ └── unit
│ │ │ ├── assets
│ │ │ ├── large.jpeg
│ │ │ └── large.png
│ │ │ └── KoenigComposer.test.jsx
│ ├── .stylelintrc.json
│ ├── public
│ │ ├── Koenig-editor-1.png
│ │ ├── Koenig-editor-2.png
│ │ └── assets
│ │ │ └── fonts
│ │ │ └── Inter.ttf
│ ├── postcss.config.cjs
│ ├── check-publish-env.js
│ ├── .storybook
│ │ ├── editorEmptyState.js
│ │ └── preview-head.html
│ ├── demo
│ │ ├── components
│ │ │ ├── icons
│ │ │ │ ├── eye-closed.svg
│ │ │ │ └── eye-open.svg
│ │ │ ├── Navigator.jsx
│ │ │ ├── DarkModeToggle.jsx
│ │ │ ├── WordCount.jsx
│ │ │ ├── FloatingButton.jsx
│ │ │ ├── Sidebar.jsx
│ │ │ └── TreeView.jsx
│ │ ├── utils
│ │ │ ├── unsplashConfig.js
│ │ │ └── tenorConfig.js
│ │ ├── assets
│ │ │ └── icons
│ │ │ │ ├── kg-lock.svg
│ │ │ │ └── kg-dollar.svg
│ │ └── content
│ │ │ └── minimal-content.json
│ ├── svgo.config.js
│ ├── .gitignore
│ └── index.html
├── html-to-mobiledoc
│ ├── index.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ └── package.json
├── kg-card-factory
│ ├── index.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ ├── README.md
│ └── package.json
├── kg-markdown-html-renderer
│ ├── index.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ └── README.md
├── kg-mobiledoc-html-renderer
│ ├── index.js
│ ├── .eslintrc.js
│ ├── test
│ │ ├── .eslintrc.js
│ │ └── utils
│ │ │ ├── index.js
│ │ │ ├── assertions.js
│ │ │ └── overrides.js
│ └── README.md
└── kg-unsplash-selector
│ ├── src
│ ├── styles
│ │ └── index.css
│ ├── vite-env.d.ts
│ ├── assets
│ │ ├── kg-card-type-unsplash.svg
│ │ ├── kg-unsplash-heart.svg
│ │ ├── kg-close.svg
│ │ ├── kg-download.svg
│ │ └── kg-search.svg
│ ├── index.ts
│ └── api
│ │ ├── unsplashFixtures.ts
│ │ └── IUnsplashProvider.ts
│ ├── demo
│ ├── vite-env.d.ts
│ └── demo.tsx
│ ├── postcss.config.cjs
│ ├── tsconfig.declaration.json
│ ├── tsconfig.node.json
│ ├── index.html
│ ├── .gitignore
│ ├── vitest.config.ts
│ └── tsconfig.json
├── .github
├── workflows
│ └── CNAME
├── hooks
│ └── pre-commit
└── renovate.json
├── .lintstagedrc
├── gh-pages.config.json
├── .vscode
└── settings.json
├── .editorconfig
└── lerna.json
/packages/kg-simplemde/.nvmrc:
--------------------------------------------------------------------------------
1 | 8
--------------------------------------------------------------------------------
/.github/workflows/CNAME:
--------------------------------------------------------------------------------
1 | https://koenig.ghost.org
--------------------------------------------------------------------------------
/packages/kg-converters/.eslintignore:
--------------------------------------------------------------------------------
1 | cjs
2 | es
3 |
--------------------------------------------------------------------------------
/packages/kg-converters/.gitignore:
--------------------------------------------------------------------------------
1 | cjs/
2 | es/
3 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/.eslintignore:
--------------------------------------------------------------------------------
1 | cjs
2 | es
3 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/.gitignore:
--------------------------------------------------------------------------------
1 | cjs/
2 | es/
3 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/.eslintignore:
--------------------------------------------------------------------------------
1 | cjs
2 | es
3 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/.eslintignore:
--------------------------------------------------------------------------------
1 | cjs
2 | es
3 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/.gitignore:
--------------------------------------------------------------------------------
1 | cjs/
2 | es/
3 |
--------------------------------------------------------------------------------
/packages/kg-converters/index.js:
--------------------------------------------------------------------------------
1 | export * from './lib/kg-converters';
2 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | tsconfig.tsbuildinfo
3 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "packages/**/*.{js,jsx,ts,tsx,cjs}": "eslint"
3 | }
--------------------------------------------------------------------------------
/packages/kg-default-nodes/index.js:
--------------------------------------------------------------------------------
1 | export * from './lib/kg-default-nodes';
2 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | tsconfig.tsbuildinfo
3 |
--------------------------------------------------------------------------------
/packages/kg-utils/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/kg-utils');
2 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/atoms');
2 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/cards');
2 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './html-to-lexical';
2 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | tsconfig.tsbuildinfo
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,ts,tsx,cjs}": "eslint"
3 | }
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/converter');
2 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/CardFactory');
2 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/.gitignore:
--------------------------------------------------------------------------------
1 | cjs/
2 | es/
3 | build/
4 | tsconfig.tsbuildinfo
--------------------------------------------------------------------------------
/packages/kg-default-transforms/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './default-transforms.js';
2 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/src/decs.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@tryghost/kg-default-nodes';
2 |
--------------------------------------------------------------------------------
/packages/kg-utils/lib/kg-utils.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | slugify: require('./slugify')
3 | };
4 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/markdown-html-renderer');
2 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/MobiledocHtmlRenderer');
2 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/isGif.js:
--------------------------------------------------------------------------------
1 | export function isGif(url) {
2 | return /\.(gif)$/.test(url);
3 | }
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/utils/isTestEnv.js:
--------------------------------------------------------------------------------
1 | export const isTestEnv = import.meta.env.VITE_TEST === 'true';
2 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/styles/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/ctrlOrCmd.js:
--------------------------------------------------------------------------------
1 | export default navigator.userAgent.indexOf('Mac') !== -1 ? 'Cmd' : 'Ctrl';
2 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/test-setup.js:
--------------------------------------------------------------------------------
1 | // add custom jest matchers for DOM state
2 | import '@testing-library/jest-dom';
3 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/lib/atoms/index.js:
--------------------------------------------------------------------------------
1 | const softReturn = require('./soft-return');
2 |
3 | module.exports = [softReturn];
4 |
--------------------------------------------------------------------------------
/packages/kg-simplemde/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "at-rule-disallowed-list": ["apply"]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/constants.js:
--------------------------------------------------------------------------------
1 | export const IMAGE_EXTENSIONS = ['gif', 'jpg', 'jpeg', 'png', 'svg', 'svgz', 'webp'];
2 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/index.ts:
--------------------------------------------------------------------------------
1 | import LexicalHTMLRenderer from './LexicalHTMLRenderer';
2 |
3 | export = LexicalHTMLRenderer;
4 |
--------------------------------------------------------------------------------
/packages/kg-utils/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/public/Koenig-editor-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/public/Koenig-editor-1.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/public/Koenig-editor-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/public/Koenig-editor-2.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/video.mp4
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/unit/assets/large.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/unit/assets/large.jpeg
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/unit/assets/large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/unit/assets/large.png
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-utils/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/public/assets/fonts/Inter.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/public/assets/fonts/Inter.ttf
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-simplemde/.gitignore:
--------------------------------------------------------------------------------
1 | localtesting/
2 | node_modules/
3 | bower_components/
4 |
5 | #For IDE
6 | *.iml
7 | *.ipr
8 | *.iws
9 | .idea/
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/print-img.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/print-img.pdf
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/video-fail.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/video-fail.mp4
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/is-unsplash-image.js:
--------------------------------------------------------------------------------
1 | module.exports = function isUnsplashImage(url) {
2 | return /images\.unsplash\.com/.test(url);
3 | };
4 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/is-unsplash-image.js:
--------------------------------------------------------------------------------
1 | export const isUnsplashImage = function (url) {
2 | return /images\.unsplash\.com/.test(url);
3 | };
4 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/audio-sample.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/audio-sample.mp3
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image.jpeg
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/plugins/assets/large.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/plugins/assets/large.jpeg
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/plugins/assets/large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/plugins/assets/large.png
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/context/CardContext.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const CardContext = React.createContext({});
4 |
5 | export default CardContext;
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-0.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-1.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-2.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-3.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-4.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-5.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-6.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-7.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-8.png
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-9.png
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/at-link/index.js:
--------------------------------------------------------------------------------
1 | /* c8 ignore start */
2 | export * from './AtLinkNode';
3 | export * from './AtLinkSearchNode';
4 | /* c8 ignore end */
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/audio-sample-fail.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/audio-sample-fail.mp3
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/e2e/fixtures/large-image-fail.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TryGhost/Koenig/HEAD/packages/koenig-lexical/test/e2e/fixtures/large-image-fail.jpeg
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node*/
2 | module.exports = {
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/test'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node*/
2 | module.exports = {
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/test'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/cards/hr.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'hr',
3 | type: 'dom',
4 |
5 | render({env: {dom}}) {
6 | return dom.createElement('hr');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/ts'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/demo/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | interface ImportMeta {
2 | readonly env: {
3 | readonly VITE_APP_DEMO: string;
4 | readonly VITE_APP_TESTING: string;
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/context/KoenigComposerContext.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const KoenigComposerContext = React.createContext({});
4 |
5 | export default KoenigComposerContext;
6 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/ts'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/kg-utils.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@tryghost/kg-utils' {
2 | export function slugify (text: string, options?: {ghostVersion?: string, type?: string}): string;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/dedent.js:
--------------------------------------------------------------------------------
1 | module.exports = function dedent(str) {
2 | let lines = str.split(/\n/);
3 | return lines.map(line => line.replace(/^\s+/gm, '')).join('').trim();
4 | };
5 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/test'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | plugins: ['ghost'],
4 | extends: [
5 | 'plugin:ghost/test'
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/gh-pages.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Koenig Demo",
3 | "description": "Koenig Demo",
4 | "baseUrl": "/Koenig/",
5 | "repository": {
6 | "url": "https://github.com/TryGhost/Koenig"
7 | }
8 | }
--------------------------------------------------------------------------------
/packages/kg-default-atoms/lib/atoms/soft-return.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'soft-return',
3 | type: 'dom',
4 | render(opts) {
5 | return opts.env.dom.createElement('br');
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/SlashMenu.jsx:
--------------------------------------------------------------------------------
1 | export function SlashMenu({children}) {
2 | return (
3 |
4 | {children}
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/cards/paywall.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'paywall',
3 | type: 'dom',
4 |
5 | render({env: {dom}}) {
6 | return dom.createComment('members-only');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/packages/kg-converters/lib/kg-converters.js:
--------------------------------------------------------------------------------
1 | import {lexicalToMobiledoc} from './lexical-to-mobiledoc';
2 | import {mobiledocToLexical} from './mobiledoc-to-lexical';
3 |
4 | export {lexicalToMobiledoc, mobiledocToLexical};
5 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | 'postcss-import': {},
4 | 'tailwindcss/nesting': {},
5 | tailwindcss: {},
6 | autoprefixer: {}
7 | }
8 | };
--------------------------------------------------------------------------------
/packages/koenig-lexical/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | 'postcss-import': {},
4 | 'tailwindcss/nesting': {},
5 | tailwindcss: {},
6 | autoprefixer: {}
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/cards/HorizontalRuleCard.jsx:
--------------------------------------------------------------------------------
1 | export function HorizontalRuleCard() {
2 | return (
3 |
4 | );
5 | }
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getImageFilenameFromSrc.js:
--------------------------------------------------------------------------------
1 | export function getImageFilenameFromSrc(src) {
2 | const url = new URL(src);
3 | const fileName = url.pathname.match(/\/([^/]*)$/)[1];
4 | return fileName;
5 | }
--------------------------------------------------------------------------------
/packages/kg-converters/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ],
6 | parserOptions: {
7 | sourceType: 'module'
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-add.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/ReadOnlyOverlay.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function ReadOnlyOverlay() {
4 | return
;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/openFileSelection.js:
--------------------------------------------------------------------------------
1 | // Triggers the file selection dialog from a given referenced element
2 |
3 | export function openFileSelection({fileInputRef}) {
4 | fileInputRef.current?.click();
5 | }
6 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/test'
5 | ],
6 | parserOptions: {
7 | sourceType: 'module'
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/check-publish-env.js:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 |
3 | if (!process.env.VITE_SENTRY_AUTH_TOKEN) {
4 | throw new Error('VITE_SENTRY_AUTH_TOKEN is not set in your .env file - this is required when publishing');
5 | }
6 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/assets/kg-card-type-unsplash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-align-center.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-align-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-unsplash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-heading-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/index.js:
--------------------------------------------------------------------------------
1 | // publicly exported util functions
2 | export * from './$isAtStartOfDocument';
3 | export * from './$selectDecoratorNode';
4 | export * from './$isAtTopOfNode';
5 | export * from './getTopLevelNativeElement';
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-divider.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/storybook/populate-storybook-editor.js:
--------------------------------------------------------------------------------
1 | import generateEditorState from '../generateEditorState';
2 |
3 | export default function populateEditor({editor, initialHtml}) {
4 | generateEditorState({editor, initialHtml});
5 | }
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-heading-3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-italic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@tryghost/kg-default-nodes' {
2 | import {DEFAULT_NODES, ExtendedHeadingNode, ImageNode} from '@tryghost/kg-default-nodes';
3 |
4 | export {DEFAULT_NODES, ExtendedHeadingNode, ImageNode};
5 | }
6 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/assets/kg-unsplash-heart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-play.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-unsplash-heart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/nodes/MinimalNodes.js:
--------------------------------------------------------------------------------
1 | import {LinkNode} from '@lexical/link';
2 | import {TKNode} from '@tryghost/kg-default-nodes';
3 |
4 | const MINIMAL_NODES = [
5 | LinkNode,
6 | TKNode
7 | ];
8 |
9 | export default MINIMAL_NODES;
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/test/unit/KoenigComposer.test.jsx:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'vitest';
2 |
3 | describe('KoenigComposer', function () {
4 | it.todo('renders');
5 | it.todo('accepts initialState prop');
6 | it.todo('accepts onChange prop');
7 | });
8 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-html.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getAccentColor.js:
--------------------------------------------------------------------------------
1 | export function getAccentColor() {
2 | const editor = document.body.querySelector('.koenig-lexical');
3 |
4 | return (editor && getComputedStyle(editor).getPropertyValue('--kg-accent-color')) || '#ff0095';
5 | }
6 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/index.ts:
--------------------------------------------------------------------------------
1 | export {UnsplashSearchModal} from './UnsplashSearchModal';
2 | export type {DefaultHeaderTypes, Photo as PhotoType} from './UnsplashTypes';
3 | export {UnsplashProvider} from './api/UnsplashProvider';
4 | import './styles/index.css';
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-arrow-top-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/assets/kg-close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/assets/kg-download.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/.storybook/editorEmptyState.js:
--------------------------------------------------------------------------------
1 | export const editorEmptyState = JSON.stringify({"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}});
2 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-email-cta.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-download.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-indicator-html.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/api/unsplashFixtures.ts:
--------------------------------------------------------------------------------
1 | // purely for testing purposes
2 | import fixturePhotosDataset from './dataFixtures.json';
3 |
4 | import {Photo} from '../UnsplashTypes';
5 |
6 | export const fixturePhotos: Photo[] = fixturePhotosDataset as unknown as Photo[];
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/demo/demo.tsx:
--------------------------------------------------------------------------------
1 | import App from './App';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom/client';
4 | ReactDOM.createRoot(document.getElementById('root')!).render(
5 |
6 |
7 |
8 | );
9 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-bold.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/tsconfig.declaration.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false,
5 | "declaration": true,
6 | "emitDeclarationOnly": true,
7 | "declarationDir": "./types"
8 | },
9 | "include": ["src"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-bookmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-quote.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getTopLevelNativeElement.js:
--------------------------------------------------------------------------------
1 | export function getTopLevelNativeElement(node) {
2 | if (node.nodeType === Node.TEXT_NODE) {
3 | node = node.parentNode;
4 | }
5 |
6 | const selector = '[data-lexical-editor] > *';
7 | return node.closest(selector);
8 | }
--------------------------------------------------------------------------------
/packages/kg-utils/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-collection.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-arrow-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts", "package.json"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-x.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-button.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-quote-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/is-local-content-image.js:
--------------------------------------------------------------------------------
1 | export const isLocalContentImage = function (url, siteUrl = '') {
2 | const normalizedSiteUrl = siteUrl.replace(/\/$/, '');
3 | const imagePath = url.replace(normalizedSiteUrl, '');
4 | return /^(\/.*|__GHOST_URL__)\/?content\/images\//.test(imagePath);
5 | };
6 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/src/kg-default-nodes.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@tryghost/kg-default-nodes' {
2 | import {ElementNode, TextNode} from 'lexical';
3 |
4 | export class AtLinkNode extends ElementNode {}
5 | export class AtLinkSearchNode extends TextNode {}
6 | export class ZWNJNode extends TextNode {}
7 | }
8 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-header.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-edit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/is-local-content-image.js:
--------------------------------------------------------------------------------
1 | module.exports = function isLocalContentImage(url, siteUrl = '') {
2 | const normalizedSiteUrl = siteUrl.replace(/\/$/, '');
3 | const imagePath = url.replace(normalizedSiteUrl, '');
4 | return /^(\/.*|__GHOST_URL__)\/?content\/images\//.test(imagePath);
5 | };
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-file-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-snippet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-unmute.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | {
4 | "pattern": "./packages/*/"
5 | }
6 | ],
7 | // autofix on save lint
8 | "editor.codeActionsOnSave": {
9 | "source.fixAll.eslint": "explicit"
10 | },
11 | "search.exclude": {
12 | "**/node_modules": true,
13 | "**/dist": true}
14 | }
15 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/assets/kg-search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-gif.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/$selectDecoratorNode.js:
--------------------------------------------------------------------------------
1 | import {
2 | $createNodeSelection,
3 | $setSelection
4 | } from 'lexical';
5 |
6 | export function $selectDecoratorNode(node) {
7 | const nodeSelection = $createNodeSelection();
8 | nodeSelection.add(node.getKey());
9 | $setSelection(nodeSelection);
10 | }
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-snippet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-video.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/icons/eye-closed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-product.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-img-wide.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-img-regular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/nodes/BasicNodes.js:
--------------------------------------------------------------------------------
1 | import {LinkNode} from '@lexical/link';
2 | import {ListItemNode, ListNode} from '@lexical/list';
3 | import {TKNode} from '@tryghost/kg-default-nodes';
4 |
5 | const BASIC_NODES = [
6 | ListNode,
7 | ListItemNode,
8 | LinkNode,
9 | TKNode
10 | ];
11 |
12 | export default BASIC_NODES;
13 |
--------------------------------------------------------------------------------
/packages/kg-utils/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-other.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-layout-split.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-eyedropper.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-indicator-markdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/icons/eye-open.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-img-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-shrink.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Should Assertions
3 | *
4 | * Add any custom assertions to this file.
5 | */
6 |
7 | // Example Assertion
8 | // should.Assertion.add('ExampleAssertion', function () {
9 | // this.params = {operator: 'to be a valid Example Assertion'};
10 | // this.obj.should.be.an.Object;
11 | // });
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/Navigator.jsx:
--------------------------------------------------------------------------------
1 | import {useNavigate} from 'react-router-dom';
2 |
3 | const Navigator = () => {
4 | const navigate = useNavigate();
5 |
6 | // Hack, used to allow Playwright to navigate without triggering a full page reload.
7 | window.navigate = navigate;
8 |
9 | return null;
10 | };
11 |
12 | export default Navigator;
13 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/KoenigDecoratorNode.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable ghost/filenames/match-exported-class */
2 | /* c8 ignore start */
3 | import {DecoratorNode} from 'lexical';
4 |
5 | export class KoenigDecoratorNode extends DecoratorNode {
6 | }
7 |
8 | export function $isKoenigCard(node) {
9 | return node instanceof KoenigDecoratorNode;
10 | }
11 | /* c8 ignore end */
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-toggle-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/horizontalrule/horizontalrule-parser.js:
--------------------------------------------------------------------------------
1 | export function parseHorizontalRuleNode(HorizontalRuleNode) {
2 | return {
3 | hr: () => ({
4 | conversion() {
5 | const node = new HorizontalRuleNode();
6 | return {node};
7 | },
8 | priority: 0
9 | })
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-star.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/src/api/IUnsplashProvider.ts:
--------------------------------------------------------------------------------
1 | import {Photo} from '../UnsplashTypes';
2 |
3 | export interface IUnsplashProvider {
4 | fetchPhotos(): Promise;
5 | fetchNextPage(): Promise;
6 | searchPhotos(term: string): Promise;
7 | triggerDownload(photo: Photo): Promise | void;
8 | searchIsRunning(): boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-layout-immersive.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/horizontalrule/horizontalrule-renderer.js:
--------------------------------------------------------------------------------
1 | import {addCreateDocumentOption} from '../../utils/add-create-document-option';
2 |
3 | export function renderHorizontalRuleNode(_, options = {}) {
4 | addCreateDocumentOption(options);
5 | const document = options.createDocument();
6 |
7 | const element = document.createElement('hr');
8 | return {element};
9 | }
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ghost Unsplash Selector
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-gallery-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/shortcutSymbols.js:
--------------------------------------------------------------------------------
1 | function isMac() {
2 | return navigator.userAgent.indexOf('Mac') !== -1;
3 | }
4 |
5 | export function ctrlOrCmdSymbol() {
6 | return isMac() ? '⌘' : 'Ctrl';
7 | }
8 |
9 | export function ctrlOrSymbol() {
10 | return isMac() ? '⌃' : 'Ctrl';
11 | }
12 |
13 | export function altOrOption() {
14 | return isMac() ? '⌥' : 'Alt';
15 | }
--------------------------------------------------------------------------------
/.github/hooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Modified from https://github.com/chaitanyagupta/gitutils
3 |
4 | [ -n "$CI" ] && exit 0
5 |
6 | GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
7 | if [ "$GIT_BRANCH" = "main" ]; then
8 | yarn lint-staged --relative
9 | lintStatus=$?
10 |
11 | if [ $lintStatus -ne 0 ]; then
12 | echo "❌ Linting failed"
13 | exit 1
14 | fi
15 | fi
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/escape-html.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Escape HTML special characters
3 | * @param {string} unsafe
4 | * @returns string
5 | */
6 | export function escapeHtml(unsafe) {
7 | return unsafe
8 | .replace(/&/g, '&')
9 | .replace(//g, '>')
11 | .replace(/"/g, '"')
12 | .replace(/'/g, ''');
13 | }
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node',
5 | 'plugin:ghost/ts',
6 | 'plugin:ghost/ts-test'
7 | ],
8 | // this fouls up with Lexical's need to prefix with '$' for lifecycle hooks
9 | rules: {
10 | 'ghost/filenames/match-exported-class': 'off'
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/test/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Utilities
3 | *
4 | * Shared utils for writing tests
5 | */
6 |
7 | // Require overrides - these add globals for tests
8 | require('./overrides');
9 |
10 | // Require assertions - adds custom should assertions
11 | require('./assertions');
12 |
13 | module.exports = {
14 | shouldRender: require('./should-render')
15 | };
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-file-upload.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-product-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/EditorPlaceholder.jsx:
--------------------------------------------------------------------------------
1 | export function EditorPlaceholder({className, text}) {
2 | return (
3 | {typeof text === 'string' ? text : 'Begin writing your post...'}
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-quote-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/TextInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function TextInput({value, onChange, ...args}) {
4 | const handleOnChange = (e) => {
5 | onChange(e);
6 | };
7 |
8 | return (
9 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/prettifyFileName.js:
--------------------------------------------------------------------------------
1 | export default function prettifyFileName(filename) {
2 | if (!filename || typeof filename !== 'string') {
3 | return '';
4 | }
5 | let updatedName = filename.split('.').slice(0, -1).join('.').replace(/[-_]/g,' ').replace(/[^\w\s]+/g,'').replace(/\s\s+/g, ' ');
6 | return updatedName.charAt(0).toUpperCase() + updatedName.slice(1);
7 | }
8 |
--------------------------------------------------------------------------------
/packages/kg-utils/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-audio.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-markdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/utils/unsplashConfig.js:
--------------------------------------------------------------------------------
1 | const API_VERSION = 'v1';
2 | const API_TOKEN = '8672af113b0a8573edae3aa3713886265d9bb741d707f6c01a486cde8c278980';
3 |
4 | export const defaultHeaders = {
5 | Authorization: `Client-ID ${API_TOKEN}`,
6 | 'Accept-Version': API_VERSION,
7 | 'Content-Type': 'application/json',
8 | 'App-Pragma': 'no-cache',
9 | 'X-Unsplash-Cache': true
10 | };
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-img-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/html-to-mobiledoc/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/plugins/TKCountPlugin.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTKContext} from '../context/TKContext';
3 |
4 | export default function TKCountPlugin({onChange}) {
5 | const {tkCount} = useTKContext();
6 |
7 | React.useEffect(() => {
8 | if (!onChange) {
9 | return;
10 | }
11 |
12 | onChange(tkCount);
13 | }, [onChange, tkCount]);
14 | }
15 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/test/test-utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/test/utils/overrides.js:
--------------------------------------------------------------------------------
1 | // This file is required before any test is run
2 |
3 | // Taken from the should wiki, this is how to make should global
4 | // Should is a global in our eslint test config
5 | global.should = require('should').noConflict();
6 | should.extend();
7 |
8 | // Sinon is a simple case
9 | // Sinon is a global in our eslint test config
10 | global.sinon = require('sinon');
11 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/CardVisibilityMessage.jsx:
--------------------------------------------------------------------------------
1 | export function CardVisibilityMessage({message}) {
2 | if (!message) {
3 | return null;
4 | }
5 |
6 | return (
7 |
8 | {message}
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/packages/kg-simplemde/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "indent": [
4 | 2,
5 | "tab"
6 | ],
7 | "strict": 0,
8 | "no-console": 0,
9 | "quotes": [
10 | 2,
11 | "double"
12 | ],
13 | "semi": [
14 | 2,
15 | "always"
16 | ],
17 | "no-useless-escape": 0
18 | },
19 | "env": {
20 | "browser": true,
21 | "node":true
22 | },
23 | "extends": "eslint:recommended"
24 | }
25 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | playwright-report
27 | test-results
28 | types
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/FileUploadForm.jsx:
--------------------------------------------------------------------------------
1 | export function FileUploadForm({onFileChange, fileInputRef}) {
2 | return (
3 |
11 | );
12 | }
13 |
14 | export default FileUploadForm;
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/analytics.js:
--------------------------------------------------------------------------------
1 | // Wrapper function for Plausible event
2 |
3 | export default function trackEvent(eventName, props = {}) {
4 | window.plausible = window.plausible || function () {
5 | (window.plausible.q = window.plausible.q || []).push(arguments);
6 | };
7 | window.plausible(eventName, {props: props});
8 | if (window.posthog) {
9 | window.posthog.capture(eventName, props);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/slugify.js:
--------------------------------------------------------------------------------
1 | export function slugify(str) {
2 | // Remove HTML tags
3 | str = str.replace(/<[^>]*>?/gm, '');
4 |
5 | // Remove any non-word character with whitespace
6 | str = str.replace(/[^\w\s]/gi, '');
7 |
8 | // Replace any whitespace character with a dash
9 | str = str.replace(/\s+/g, '-');
10 |
11 | // Convert to lowercase
12 | str = str.toLowerCase();
13 |
14 | return str;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-signup.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-img-bg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/utils/tenorConfig.js:
--------------------------------------------------------------------------------
1 | import {isTestEnv} from '../../test/utils/isTestEnv';
2 |
3 | export const tenorConfig = isTestEnv ? {googleApiKey: 'xxx'} : getTenorConfig();
4 |
5 | function getTenorConfig() {
6 | let config = null;
7 |
8 | if (import.meta.env.VITE_TENOR_API_KEY) {
9 | config = {
10 | googleApiKey: import.meta.env.VITE_TENOR_API_KEY
11 | };
12 | }
13 |
14 | return config;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-replace.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 4
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.hbs]
14 | insert_final_newline = false
15 |
16 | [*.json]
17 | indent_size = 2
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
22 | [*.{yml,yaml}]
23 | indent_size = 2
24 |
25 | [Makefile]
26 | indent_style = tab
27 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-preview.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-toggle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-layout-list.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-swap.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-video-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/hbs.js:
--------------------------------------------------------------------------------
1 | const Handlebars = require('handlebars');
2 |
3 | module.exports = function hbs(literals, ...values) {
4 | // interweave strings with substitutions
5 | let output = '';
6 | for (let i = 0; i < values.length; i++) {
7 | output += literals[i] + values[i];
8 | }
9 | output += literals[values.length];
10 |
11 | // return compiled handlebars template
12 | return Handlebars.compile(output);
13 | };
14 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/DarkModeToggle.jsx:
--------------------------------------------------------------------------------
1 | const DarkModeToggle = ({darkMode, toggleDarkMode}) => {
2 | return (
3 | <>
4 |
5 | {darkMode ? '🌚' : '🌞'}
6 |
7 | >
8 | );
9 | };
10 |
11 | export default DarkModeToggle;
12 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getEditorCardNodes.js:
--------------------------------------------------------------------------------
1 | export function getEditorCardNodes(editor) {
2 | // TODO: open upstream PR to add public method of getting nodes
3 | const allNodes = editor._nodes;
4 | const cardNodes = [];
5 |
6 | for (const [nodeType, {klass}] of allNodes) {
7 | if (!klass.kgMenu) {
8 | continue;
9 | }
10 |
11 | cardNodes.push([nodeType, klass]);
12 | }
13 |
14 | return cardNodes;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/svgo.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | multipass: true,
3 | plugins: [
4 | {
5 | name: 'preset-default',
6 | params: {
7 | overrides: {
8 | // viewBox is required to resize SVGs with CSS.
9 | // @see https://github.com/svg/svgo/issues/1128
10 | removeViewBox: false
11 | }
12 | }
13 | }
14 | ]
15 | };
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/isInternalUrl.js:
--------------------------------------------------------------------------------
1 | export function isInternalUrl(url, siteUrl) {
2 | if (!url || !siteUrl) {
3 | return false;
4 | }
5 |
6 | try {
7 | const urlObj = new URL(url);
8 | const subdir = `/${new URL(siteUrl).pathname.split('/')[1]}`;
9 | return urlObj.hostname === new URL(siteUrl).hostname
10 | && urlObj.pathname.startsWith(subdir);
11 | } catch (e) {
12 | return false;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 | storybook-static
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | /test-results/
26 | /playwright-report/
27 | /playwright/.cache/
28 | .env
29 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/assets/icons/kg-lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/autoExpandTextArea.js:
--------------------------------------------------------------------------------
1 | import {useEffect} from 'react';
2 |
3 | const useAutoExpandTextArea = ({el, value}) => {
4 | useEffect(() => {
5 | const element = el.current;
6 | if (element) {
7 | element.style.height = '0px';
8 | const height = element.scrollHeight;
9 | element.style.height = `${height}px`;
10 | }
11 | }, [el, value]);
12 | };
13 |
14 | export default useAutoExpandTextArea;
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getAudioMetadata.js:
--------------------------------------------------------------------------------
1 | // gets image dimensions from a given Url
2 |
3 | export async function getAudioMetadata(url) {
4 | let audio = new Audio();
5 | let duration;
6 |
7 | return new Promise((resolve) => {
8 | audio.onloadedmetadata = function () {
9 | duration = audio.duration;
10 | resolve({
11 | duration
12 | });
13 | };
14 | audio.src = url;
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/plugins/CardMenuPlugin.jsx:
--------------------------------------------------------------------------------
1 | import PlusCardMenuPlugin from '../plugins/PlusCardMenuPlugin';
2 | import React from 'react';
3 | import SlashCardMenuPlugin from '../plugins/SlashCardMenuPlugin';
4 |
5 | export const CardMenuPlugin = () => {
6 | return (
7 | <>
8 | {/* Koenig Plugins */}
9 |
10 |
11 | >
12 | );
13 | };
14 |
15 | export default CardMenuPlugin;
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "independent",
3 | "npmClient": "yarn",
4 | "packages": ["packages/*"],
5 | "command": {
6 | "version": {
7 | "exact": true
8 | },
9 | "publish": {
10 | "allowBranch": "main",
11 | "message": "Published new versions"
12 | }
13 | },
14 | "local": {
15 | "public": "true",
16 | "repo": "https://github.com/TryGhost/Koenig",
17 | "scope": "@tryghost"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-layout-minimal.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dedent: require('./dedent'),
3 | getAvailableImageWidths: require('./get-available-image-widths'),
4 | hbs: require('./hbs'),
5 | isLocalContentImage: require('./is-local-content-image'),
6 | isUnsplashImage: require('./is-unsplash-image'),
7 | resizeImage: require('./resize-image'),
8 | generateImgAttrs: require('./generate-img-attrs'),
9 | ...require('./srcset-attribute') // get/setSrcsetAttribute
10 | };
11 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/at-link/kg-link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/context/SharedOnChangeContext.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Context = React.createContext({});
4 |
5 | export const SharedOnChangeContext = ({onChange, children}) => {
6 | const onChangeContext = React.useMemo(
7 | () => ({onChange}),
8 | [onChange]
9 | );
10 |
11 | return {children} ;
12 | };
13 |
14 | export const useSharedOnChangeContext = () => React.useContext(Context);
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Koenig Lexical Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/lib/cards/soft-return.js:
--------------------------------------------------------------------------------
1 | export function fromBr() {
2 | // mobiledoc by default ignores tags but we have a custom SoftReturn atom
3 | return function fromBrToSoftReturnAtom(node, builder, {addMarkerable, nodeFinished}) {
4 | if (node.nodeType !== 1 || node.tagName !== 'BR') {
5 | return;
6 | }
7 |
8 | let softReturn = builder.createAtom('soft-return');
9 | addMarkerable(softReturn);
10 |
11 | nodeFinished();
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/AudioUploadForm.jsx:
--------------------------------------------------------------------------------
1 | export function AudioUploadForm({onFileChange, fileInputRef, mimeTypes = ['audio/*']}) {
2 | return (
3 |
12 | );
13 | }
14 |
15 | export default AudioUploadForm;
16 |
--------------------------------------------------------------------------------
/packages/kg-converters/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ],
6 | parser: '@babel/eslint-parser',
7 | parserOptions: {
8 | sourceType: 'module',
9 | requireConfigFile: false,
10 | babelOptions: {
11 | plugins: [
12 | '@babel/plugin-syntax-import-assertions'
13 | ]
14 | }
15 | },
16 | env: {
17 | browser: true,
18 | node: true
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/Input.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Input} from './Input';
4 |
5 | const story = {
6 | title: 'Generic/Input',
7 | component: Input,
8 | parameters: {
9 | status: {
10 | type: 'uiReady'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = args => (
17 |
18 |
19 |
20 | );
21 |
22 | export const Default = Template.bind({});
23 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ],
6 | parser: '@babel/eslint-parser',
7 | parserOptions: {
8 | sourceType: 'module',
9 | requireConfigFile: false,
10 | babelOptions: {
11 | plugins: [
12 | '@babel/plugin-syntax-import-assertions'
13 | ]
14 | }
15 | },
16 | env: {
17 | browser: true,
18 | node: true
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ],
6 | parser: '@babel/eslint-parser',
7 | parserOptions: {
8 | sourceType: 'module',
9 | requireConfigFile: false,
10 | babelOptions: {
11 | plugins: [
12 | '@babel/plugin-syntax-import-assertions'
13 | ]
14 | }
15 | },
16 | env: {
17 | browser: true,
18 | node: true
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/packages/kg-parser-plugins/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['ghost'],
3 | extends: [
4 | 'plugin:ghost/node'
5 | ],
6 | parser: '@babel/eslint-parser',
7 | parserOptions: {
8 | sourceType: 'module',
9 | requireConfigFile: false,
10 | babelOptions: {
11 | plugins: [
12 | '@babel/plugin-syntax-import-assertions'
13 | ]
14 | }
15 | },
16 | env: {
17 | browser: true,
18 | node: true
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/Toggle.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Toggle} from './Toggle';
4 |
5 | const story = {
6 | title: 'Generic/Toggle',
7 | component: Toggle,
8 | parameters: {
9 | status: {
10 | type: 'functional'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = args => (
17 |
18 | );
19 |
20 | export const Default = Template.bind({});
21 | Default.args = {
22 | isChecked: true
23 | };
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/styles/components/react-colorful.css:
--------------------------------------------------------------------------------
1 | .koenig-lexical .react-colorful {
2 | width: 100%;
3 | height: 140px;
4 |
5 | .react-colorful__saturation {
6 | border-radius: 4px 4px 0 0;
7 | border-bottom: 1px solid #000;
8 | }
9 |
10 | .react-colorful__hue {
11 | height: 8px;
12 | border-radius: 0 0 4px 4px;
13 | }
14 |
15 | .react-colorful__pointer {
16 | width: 14px;
17 | height: 14px;
18 | border-width: 1px;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-callout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getImageDimensions.js:
--------------------------------------------------------------------------------
1 | // gets image dimensions from a given Url
2 |
3 | export async function getImageDimensions(url) {
4 | const img = new Image();
5 | return new Promise((resolve, reject) => {
6 | img.onload = () => {
7 | resolve({width: img.naturalWidth, height: img.naturalHeight});
8 | };
9 | img.onerror = reject;
10 | // Set image src after listeners to avoid the image loading before the listener is set
11 | img.src = url;
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-gen-embed.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getScrollParent.js:
--------------------------------------------------------------------------------
1 | export function getScrollParent(node) {
2 | const isElement = node instanceof HTMLElement;
3 | const overflowY = isElement && window.getComputedStyle(node).overflowY;
4 | const isScrollable = overflowY !== 'visible' && overflowY !== 'hidden';
5 |
6 | if (!node) {
7 | return null;
8 | } else if (isScrollable && node.scrollHeight >= node.clientHeight) {
9 | return node;
10 | }
11 |
12 | return getScrollParent(node.parentNode) || document.body;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-wand.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/callToActionColors.js:
--------------------------------------------------------------------------------
1 | export const CALLTOACTION_COLORS = {
2 | none: 'bg-transparent border-transparent',
3 | white: 'bg-transparent border-grey-900/15 dark:border-grey-100/20',
4 | grey: 'bg-grey/10 border-transparent',
5 | blue: 'bg-blue/10 border-transparent',
6 | green: 'bg-green/10 border-transparent',
7 | yellow: 'bg-yellow/10 border-transparent',
8 | red: 'bg-red/10 border-transparent',
9 | pink: 'bg-pink/10 border-transparent',
10 | purple: 'bg-purple/10 border-transparent'
11 | };
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/transformers/element/paragraph.ts:
--------------------------------------------------------------------------------
1 | import {$isParagraphNode} from 'lexical';
2 | import type {ElementNode} from 'lexical';
3 | import type {ExportChildren} from '..';
4 | import type {RendererOptions} from '@tryghost/kg-default-nodes';
5 |
6 | module.exports = {
7 | export(node: ElementNode, options: RendererOptions, exportChildren: ExportChildren) {
8 | if (!$isParagraphNode(node)) {
9 | return null;
10 | }
11 |
12 | return `${exportChildren(node)}
`;
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/MediaPlayer.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {MediaPlayer} from './MediaPlayer';
4 |
5 | const story = {
6 | title: 'Generic/Media player',
7 | component: MediaPlayer,
8 | parameters: {
9 | status: {
10 | type: 'functional'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = args => (
17 |
18 | );
19 |
20 | export const Default = Template.bind({});
21 | Default.args = {
22 | theme: 'dark'
23 | };
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-trash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-sync.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/isEditorEmpty.js:
--------------------------------------------------------------------------------
1 | import {$canShowPlaceholderCurry} from '@lexical/text';
2 |
3 | export function isEditorEmpty(editor) {
4 | // NOTE: This feels hacky but was required because we check editor empty state
5 | // when rendering cards to determine whether to show nested editors. But
6 | // _after an undo_ at the point we check the nested editor state is not yet fully committed
7 | const editorState = editor._pendingEditorState || editor.getEditorState();
8 | return editorState.read($canShowPlaceholderCurry(false));
9 | }
10 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-audio-file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/ActionToolbar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useKoenigSelectedCardContext} from '../../context/KoenigSelectedCardContext';
3 |
4 | export function ActionToolbar({isVisible, children, ...props}) {
5 | const {isDragging} = useKoenigSelectedCardContext();
6 |
7 | if (isVisible && !isDragging) {
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/PlusMenu.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {PlusButton} from './PlusMenu';
4 |
5 | const story = {
6 | title: 'Card menu/Plus button',
7 | component: PlusButton,
8 | parameters: {
9 | status: {
10 | type: 'functional'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = args => (
17 |
20 | );
21 |
22 | export const Default = Template.bind({});
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/Slider.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Slider} from './Slider';
3 |
4 | const story = {
5 | title: 'Generic/Slider',
6 | component: Slider,
7 | parameters: {
8 | status: {
9 | type: 'functional'
10 | }
11 | }
12 | };
13 | export default story;
14 |
15 | const Template = args => (
16 |
17 | );
18 |
19 | export const Default = Template.bind({});
20 | Default.args = {
21 | min: 1,
22 | max: 10,
23 | value: 5,
24 | onChange: () => {}
25 | };
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/SubscribeForm.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {SubscribeForm} from './SubscribeForm';
4 |
5 | const story = {
6 | title: 'Generic/Subscribe form',
7 | component: SubscribeForm,
8 | parameters: {
9 | status: {
10 | type: 'uiReady'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = args => (
17 |
18 |
19 |
20 | );
21 |
22 | export const Default = Template.bind({});
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/file-selectors/UnsplashModal.jsx:
--------------------------------------------------------------------------------
1 | import Portal from '../Portal';
2 | import {UnsplashSearchModal} from '@tryghost/kg-unsplash-selector';
3 |
4 | const UnsplashModal = ({unsplashConf, onImageInsert, onClose}) => {
5 | return (
6 |
7 |
12 |
13 | );
14 | };
15 |
16 | export default UnsplashModal;
17 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/ProgressBar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {ProgressBar} from './ProgressBar';
3 |
4 | const story = {
5 | title: 'Generic/Progress bar',
6 | component: ProgressBar,
7 | parameters: {
8 | status: {
9 | type: 'functional'
10 | }
11 | }
12 | };
13 | export default story;
14 |
15 | const Template = args => (
16 |
17 | );
18 |
19 | export const Default = Template.bind({});
20 | Default.args = {
21 | style: {width: 60 + '%'},
22 | fullWidth: false
23 | };
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/paywall/paywall-renderer.js:
--------------------------------------------------------------------------------
1 | import {addCreateDocumentOption} from '../../utils/add-create-document-option';
2 |
3 | export function renderPaywallNode(_, options = {}) {
4 | addCreateDocumentOption(options);
5 | const document = options.createDocument();
6 | const element = document.createElement('div');
7 |
8 | element.innerHTML = '';
9 |
10 | // `type: 'inner'` will render only the innerHTML of the element
11 | // @see @tryghost/kg-lexical-html-renderer package
12 | return {element, type: 'inner'};
13 | }
14 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/clean-dom.js:
--------------------------------------------------------------------------------
1 | export function cleanDOM(node, allowedTags) {
2 | for (let i = 0; i < node.childNodes.length; i++) {
3 | let child = node.childNodes[i];
4 | if (child.nodeType === 1 && !allowedTags.includes(child.tagName)) {
5 | while (child.firstChild) {
6 | node.insertBefore(child.firstChild, child);
7 | }
8 | node.removeChild(child);
9 | i -= 1;
10 | } else if (child.nodeType === 1) {
11 | cleanDOM(child, allowedTags);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/paywall/paywall-parser.js:
--------------------------------------------------------------------------------
1 | export function parsePaywallNode(PaywallNode) {
2 | return {
3 | '#comment': (nodeElem) => {
4 | if (nodeElem.nodeType === 8 && nodeElem.nodeValue.trim() === 'members-only') {
5 | return {
6 | conversion() {
7 | const node = new PaywallNode();
8 | return {node};
9 | },
10 | priority: 0
11 | };
12 | }
13 | return null;
14 | }
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/context/SharedHistoryContext.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {createEmptyHistoryState} from '@lexical/react/LexicalHistoryPlugin';
3 |
4 | const Context = React.createContext({});
5 |
6 | export const SharedHistoryContext = ({children}) => {
7 | const historyContext = React.useMemo(
8 | () => ({historyState: createEmptyHistoryState()}),
9 | []
10 | );
11 |
12 | return {children} ;
13 | };
14 |
15 | export const useSharedHistoryContext = () => React.useContext(Context);
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/Delayed.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Delayed({children, waitBeforeShow = 500}) {
4 | const [show, setShow] = React.useState(waitBeforeShow === 0);
5 |
6 | React.useEffect(() => {
7 | if (show) {
8 | return;
9 | }
10 |
11 | const timeout = setTimeout(() => {
12 | setShow(true);
13 | }, waitBeforeShow);
14 |
15 | return () => {
16 | clearTimeout(timeout);
17 | };
18 | }, [show, waitBeforeShow]);
19 |
20 | return show ? children : null;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-audio-placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/kg-simplemde/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplemde",
3 | "version": "1.11.2",
4 | "homepage": "https://github.com/NextStepWebs/simplemde-markdown-editor",
5 | "authors": [
6 | "Wes Cossick "
7 | ],
8 | "description": "A simple, beautiful, and embeddable JavaScript Markdown editor.",
9 | "main": ["src/js/simplemde.js", "src/css/simplemde.css"],
10 | "keywords": [
11 | "embeddable",
12 | "markdown",
13 | "editor",
14 | "javascript",
15 | "wysiwyg"
16 | ],
17 | "license": "MIT",
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-indicator-email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-layout-grid.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/plugins/KoenigBlurPlugin.jsx:
--------------------------------------------------------------------------------
1 | import {BLUR_COMMAND, COMMAND_PRIORITY_EDITOR} from 'lexical';
2 | import {useEffect} from 'react';
3 | import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
4 |
5 | export const KoenigBlurPlugin = ({onBlur}) => {
6 | const [editor] = useLexicalComposerContext();
7 | useEffect(() => {
8 | editor.registerCommand(
9 | BLUR_COMMAND,
10 | () => {
11 | onBlur?.();
12 | },
13 | COMMAND_PRIORITY_EDITOR
14 | );
15 | }, [editor, onBlur]);
16 |
17 | return null;
18 | };
19 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/plugins/KoenigFocusPlugin.jsx:
--------------------------------------------------------------------------------
1 | import {COMMAND_PRIORITY_EDITOR, FOCUS_COMMAND} from 'lexical';
2 | import {useEffect} from 'react';
3 | import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
4 |
5 | export const KoenigFocusPlugin = ({onFocus}) => {
6 | const [editor] = useLexicalComposerContext();
7 | useEffect(() => {
8 | editor.registerCommand(
9 | FOCUS_COMMAND,
10 | () => {
11 | onFocus?.();
12 | },
13 | COMMAND_PRIORITY_EDITOR
14 | );
15 | }, [editor, onFocus]);
16 |
17 | return null;
18 | };
19 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/serializers/paragraph.js:
--------------------------------------------------------------------------------
1 | export default {
2 | import: {
3 | p: (node) => {
4 | const isGoogleDocs = !!node.closest('[id^="docs-internal-guid-"]');
5 |
6 | // Google docs wraps dividers in paragraphs, without text content
7 | // Remove them to avoid creating empty paragraphs in the editor
8 | if (isGoogleDocs && node.textContent === '') {
9 | return {
10 | conversion: () => null,
11 | priority: 1
12 | };
13 | }
14 |
15 | return null;
16 | }
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/KoenigErrorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import KoenigComposerContext from '../context/KoenigComposerContext';
2 | import React from 'react';
3 | import {ErrorBoundary as ReactErrorBoundary} from 'react-error-boundary';
4 |
5 | export default function KoenigErrorBoundary({children}) {
6 | const {onError} = React.useContext(KoenigComposerContext);
7 |
8 | return (
9 | An error was thrown.}
11 | onError={onError}
12 | >
13 | {children}
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/cards/PaywallCard.jsx:
--------------------------------------------------------------------------------
1 | export function PaywallCard() {
2 | return (
3 |
4 | Free public preview
5 | ↑
6 | /
7 | ↓
8 | Only visible to members
9 |
10 | );
11 | }
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/cards/index.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | require('./bookmark'),
3 | require('./code'),
4 | require('./email'),
5 | require('./email-cta'),
6 | require('./embed'),
7 | require('./gallery'),
8 | require('./hr'),
9 | require('./html'),
10 | require('./image'),
11 | require('./markdown'),
12 | require('./paywall'),
13 | require('./button'),
14 | require('./callout'),
15 | require('./product'),
16 | require('./toggle'),
17 | require('./audio'),
18 | require('./video'),
19 | require('./file'),
20 | require('./header'),
21 | require('./before-after')
22 | ];
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/WordCount.jsx:
--------------------------------------------------------------------------------
1 | const WordCount = ({wordCount, tkCount}) => {
2 | return (
3 |
4 | {wordCount} words
5 | {tkCount > 0 && (
6 | <>
7 | {' '}
8 | / {tkCount} TK{tkCount > 1 && 's'}
9 | >
10 | )}
11 |
12 | );
13 | };
14 |
15 | export default WordCount;
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-youtube.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-eye-closed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/IconButton.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import DeleteIcon from '../../assets/icons/kg-trash.svg?react';
4 | import {IconButton} from './IconButton';
5 |
6 | const story = {
7 | title: 'Generic/Icon button',
8 | component: IconButton,
9 | parameters: {
10 | status: {
11 | type: 'functional'
12 | }
13 | }
14 | };
15 | export default story;
16 |
17 | const Template = (args) => {
18 | return (
19 |
20 | );
21 | };
22 |
23 | export const Default = Template.bind({});
24 | Default.args = {
25 | Icon: DeleteIcon
26 | };
27 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getSelectedNode.js:
--------------------------------------------------------------------------------
1 | import {$isAtNodeEnd} from '@lexical/selection';
2 |
3 | export function getSelectedNode(selection) {
4 | const anchor = selection.anchor;
5 | const focus = selection.focus;
6 | const anchorNode = selection.anchor.getNode();
7 | const focusNode = selection.focus.getNode();
8 | if (anchorNode === focusNode) {
9 | return anchorNode;
10 | }
11 | const isBackward = selection.isBackward();
12 | if (isBackward) {
13 | return $isAtNodeEnd(focus) ? anchorNode : focusNode;
14 | } else {
15 | return $isAtNodeEnd(anchor) ? focusNode : anchorNode;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/cards/hr.test.js:
--------------------------------------------------------------------------------
1 | // Switch these lines once there are useful utils
2 | // const testUtils = require('./utils');
3 | require('../utils');
4 |
5 | const card = require('../../lib/cards/hr');
6 | const SimpleDom = require('simple-dom');
7 | const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
8 |
9 | describe('HR card', function () {
10 | it('generates a horizontal rule', function () {
11 | let opts = {
12 | env: {
13 | dom: new SimpleDom.Document()
14 | }
15 | };
16 |
17 | serializer.serialize(card.render(opts)).should.match(' ');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/build-clean-basic-html-for-element.js:
--------------------------------------------------------------------------------
1 | import cleanBasicHtml from '@tryghost/kg-clean-basic-html';
2 |
3 | export function buildCleanBasicHtmlForElement(domNode) {
4 | return function _cleanBasicHtml(html, additionalOptions = {}) {
5 | const cleanedHtml = cleanBasicHtml(html, {
6 | createDocument: (_html) => {
7 | const newDoc = domNode.ownerDocument.implementation.createHTMLDocument();
8 | newDoc.body.innerHTML = _html;
9 | return newDoc;
10 | },
11 | ...additionalOptions
12 | });
13 | return cleanedHtml;
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/email/EmailNode.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable ghost/filenames/match-exported-class */
2 | import {generateDecoratorNode} from '../../generate-decorator-node';
3 | import {renderEmailNode} from './email-renderer';
4 |
5 | export class EmailNode extends generateDecoratorNode({
6 | nodeType: 'email',
7 | properties: [
8 | {name: 'html', default: '', urlType: 'html'}
9 | ],
10 | defaultRenderFn: renderEmailNode
11 | }) {
12 | }
13 |
14 | export const $createEmailNode = (dataset) => {
15 | return new EmailNode(dataset);
16 | };
17 |
18 | export function $isEmailNode(node) {
19 | return node instanceof EmailNode;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/lib/utils/is-unsplash-image.test.js:
--------------------------------------------------------------------------------
1 | // Switch these lines once there are useful utils
2 | // const testUtils = require('./utils');
3 | require('../../utils');
4 |
5 | const isUnsplashImage = require('../../../lib/utils/is-unsplash-image');
6 |
7 | describe('Utils: isUnsplashImage', function () {
8 | it('returns true when url matches unsplash url', function () {
9 | isUnsplashImage('https://images.unsplash.com/test').should.be.true();
10 | });
11 |
12 | it('returns false when url does not match unsplash url', function () {
13 | isUnsplashImage('https://images.example.com/test').should.be.false();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/ProgressBar.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 |
4 | export function ProgressBar({style, fullWidth, bgStyle}) {
5 | return (
6 |
9 | );
10 | }
11 |
12 | ProgressBar.propTypes = {
13 | style: PropTypes.object,
14 | fullWidth: PropTypes.bool
15 | };
16 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/hooks/useClickOutside.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function useClickOutside(enabled, ref, handler) {
4 | React.useEffect(() => {
5 | if (!enabled) {
6 | return;
7 | }
8 |
9 | const handleClickOutside = (event) => {
10 | if (ref.current && !ref.current.contains(event.target)) {
11 | handler();
12 | }
13 | };
14 |
15 | window.addEventListener('mousedown', handleClickOutside, {capture: true});
16 | return () => window.removeEventListener('mousedown', handleClickOutside, {capture: true});
17 | }, [enabled, handler, ref]);
18 | }
19 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/test/atoms/soft-return.test.js:
--------------------------------------------------------------------------------
1 | // Switch these lines once there are useful utils
2 | // const testUtils = require('../utils');
3 | require('../utils');
4 |
5 | const atom = require('../../lib/atoms/soft-return');
6 | const SimpleDom = require('simple-dom');
7 | const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
8 |
9 | describe('Soft return atom', function () {
10 | it('generates a `br` tag', function () {
11 | let opts = {
12 | env: {
13 | dom: new SimpleDom.Document()
14 | }
15 | };
16 |
17 | serializer.serialize(atom.render(opts)).should.match(' ');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/thumbnailUploadHandler.js:
--------------------------------------------------------------------------------
1 | import {$getNodeByKey} from 'lexical';
2 |
3 | export const thumbnailUploadHandler = async (files, nodeKey, editor, upload) => {
4 | if (!files) {
5 | return;
6 | }
7 |
8 | let mediaSrc = '';
9 |
10 | editor.getEditorState().read(() => {
11 | const node = $getNodeByKey(nodeKey);
12 | mediaSrc = node.src;
13 | });
14 |
15 | const uploadResult = await upload(files, {formData: {url: mediaSrc}});
16 |
17 | await editor.update(() => {
18 | const node = $getNodeByKey(nodeKey);
19 | node.thumbnailSrc = uploadResult[0].url;
20 | });
21 |
22 | return;
23 | };
24 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/lib/utils/resize-image.js:
--------------------------------------------------------------------------------
1 | module.exports = function resizeImage(image, {width: desiredWidth, height: desiredHeight} = {}) {
2 | const {width, height} = image;
3 | const ratio = width / height;
4 |
5 | if (desiredWidth) {
6 | const resizedHeight = Math.round(desiredWidth / ratio);
7 |
8 | return {
9 | width: desiredWidth,
10 | height: resizedHeight
11 | };
12 | }
13 |
14 | if (desiredHeight) {
15 | const resizedWidth = Math.round(desiredHeight * ratio);
16 |
17 | return {
18 | width: resizedWidth,
19 | height: desiredHeight
20 | };
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/Tooltip.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Tooltip({label, shortcutKeys}) {
4 | return (
5 |
6 |
{label}
7 | {shortcutKeys && shortcutKeys.map(k => (
8 |
{k}
9 | ))}
10 |
11 | );
12 | }
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/LinkToolbar.stories.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/jsx-key */
2 | import React from 'react';
3 |
4 | import {LinkToolbar} from './LinkToolbar';
5 |
6 | const story = {
7 | title: 'Toolbar/LinkToolbar',
8 | component: LinkToolbar,
9 | parameters: {
10 | status: {
11 | type: 'functional'
12 | }
13 | }
14 | };
15 | export default story;
16 |
17 | const Template = (args) => {
18 | return (
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export const Base = Template.bind({});
26 | Base.args = {
27 | href: 'https://ghost.org/'
28 | };
29 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/read-caption-from-element.js:
--------------------------------------------------------------------------------
1 | import {buildCleanBasicHtmlForElement} from './build-clean-basic-html-for-element';
2 |
3 | export function readCaptionFromElement(element, {selector = 'figcaption'} = {}) {
4 | const cleanBasicHtml = buildCleanBasicHtmlForElement(element);
5 |
6 | let caption;
7 |
8 | const figcaptions = Array.from(element.querySelectorAll(selector));
9 | if (figcaptions.length) {
10 | figcaptions.forEach((figcaption) => {
11 | const cleanHtml = cleanBasicHtml(figcaption.innerHTML);
12 | caption = caption ? `${caption} / ${cleanHtml}` : cleanHtml;
13 | });
14 | }
15 |
16 | return caption;
17 | }
18 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/render-empty-container.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Renders an empty container element
3 | * In the returned object, `type: 'inner'` is picked up by the `@tryghost/kg-lexical-html-renderer` package
4 | * to render the inner content of the container element (in this case, nothing)
5 | *
6 | * @see @tryghost/kg-lexical-html-renderer package
7 | * @see https://github.com/TryGhost/Koenig/blob/e14c008e176f7a1036fe3f3deb924ed69a69191f/packages/kg-lexical-html-renderer/lib/convert-to-html-string.js#L29
8 | */
9 | export function renderEmptyContainer(document) {
10 | const emptyContainer = document.createElement('span');
11 | return {element: emptyContainer, type: 'inner'};
12 | }
13 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/src/transforms/remove-alignment.ts:
--------------------------------------------------------------------------------
1 | import {ElementNode, Klass, LexicalEditor} from 'lexical';
2 |
3 | export function removeAlignmentTransform(node: ElementNode) {
4 | // on element nodes format===text-align in Lexical
5 | if (node.getFormatType() !== '') {
6 | node.setFormat('');
7 | }
8 | }
9 |
10 | export function registerRemoveAlignmentTransform(editor: LexicalEditor, klass: Klass) {
11 | if (editor.hasNodes([klass])) {
12 | return editor.registerNodeTransform(klass, removeAlignmentTransform);
13 | }
14 |
15 | // eslint-disable-next-line @typescript-eslint/no-empty-function
16 | return () => {};
17 | }
18 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/transformers/index.ts:
--------------------------------------------------------------------------------
1 | import {ElementNode} from 'lexical';
2 | import type {RendererOptions} from '@tryghost/kg-default-nodes';
3 |
4 | export type ExportChildren = (node: ElementNode, options?: RendererOptions) => string;
5 | export type ElementTransformer = {
6 | export: (node: ElementNode, options: RendererOptions, exportChildren: ExportChildren) => string | null;
7 | };
8 |
9 | const elementTransformers: ElementTransformer[] = [
10 | require('./element/paragraph'),
11 | require('./element/heading'),
12 | require('./element/list'),
13 | require('./element/blockquote'),
14 | require('./element/aside')
15 | ];
16 |
17 | export default elementTransformers;
18 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-gallery.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/get-resized-image-dimensions.js:
--------------------------------------------------------------------------------
1 | export const getResizedImageDimensions = function (image, {width: desiredWidth, height: desiredHeight} = {}) {
2 | const {width, height} = image;
3 | const ratio = width / height;
4 |
5 | if (desiredWidth) {
6 | const resizedHeight = Math.round(desiredWidth / ratio);
7 |
8 | return {
9 | width: desiredWidth,
10 | height: resizedHeight
11 | };
12 | }
13 |
14 | if (desiredHeight) {
15 | const resizedWidth = Math.round(desiredHeight * ratio);
16 |
17 | return {
18 | width: resizedWidth,
19 | height: desiredHeight
20 | };
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-upload-fill.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/ImageUploadForm.jsx:
--------------------------------------------------------------------------------
1 | export function ImageUploadForm({onFileChange, fileInputRef, mimeTypes = ['image/*'], multiple = false, disabled}) {
2 | const accept = mimeTypes.join(',');
3 |
4 | return (
5 |
17 | );
18 | }
19 |
20 | export default ImageUploadForm;
21 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/test/utils/should-render.js:
--------------------------------------------------------------------------------
1 | const jsdom = require('jsdom');
2 | const {JSDOM} = jsdom;
3 | const Renderer = require('../../');
4 |
5 | const dom = new JSDOM();
6 |
7 | function shouldRender({input, output, options = {}}) {
8 | return async function () {
9 | const defaultOnError = (err) => {
10 | throw err;
11 | };
12 |
13 | const {nodes, onError, ...renderOptions} = options;
14 | const renderer = new Renderer({dom, nodes, onError: onError || defaultOnError});
15 | const renderedInput = await renderer.render(input, renderOptions);
16 | renderedInput.should.equal(output);
17 | };
18 | }
19 |
20 | module.exports = shouldRender;
21 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/markdown/markdown-renderer.js:
--------------------------------------------------------------------------------
1 | import markdownHtmlRenderer from '@tryghost/kg-markdown-html-renderer';
2 | import {addCreateDocumentOption} from '../../utils/add-create-document-option';
3 |
4 | export function renderMarkdownNode(node, options = {}) {
5 | addCreateDocumentOption(options);
6 | const document = options.createDocument();
7 |
8 | const html = markdownHtmlRenderer.render(node.markdown || '', options);
9 |
10 | const element = document.createElement('div');
11 | element.innerHTML = html;
12 |
13 | // `type: 'inner'` will render only the innerHTML of the element
14 | // @see @tryghost/kg-lexical-html-renderer package
15 | return {element, type: 'inner'};
16 | }
17 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/paywall/PaywallNode.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable ghost/filenames/match-exported-class */
2 | import {generateDecoratorNode} from '../../generate-decorator-node';
3 | import {parsePaywallNode} from './paywall-parser';
4 | import {renderPaywallNode} from './paywall-renderer';
5 |
6 | export class PaywallNode extends generateDecoratorNode({
7 | nodeType: 'paywall',
8 | defaultRenderFn: renderPaywallNode
9 | }) {
10 | static importDOM() {
11 | return parsePaywallNode(this);
12 | }
13 | }
14 |
15 | export const $createPaywallNode = (dataset) => {
16 | return new PaywallNode(dataset);
17 | };
18 |
19 | export function $isPaywallNode(node) {
20 | return node instanceof PaywallNode;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/tagged-template-fns.mjs:
--------------------------------------------------------------------------------
1 | const oneline = function (strings, ...values) {
2 | // Handle case where a plain string is passed
3 | if (typeof strings === 'string') {
4 | return strings.replace(/\n\s+/g, '').trim();
5 | }
6 |
7 | // Handle tagged template literal case
8 | const result = strings.reduce((acc, str, i) => {
9 | return acc + str + (values[i] || '');
10 | }, '');
11 | // Remove newline+indentation patterns while preserving intentional whitespace
12 | return result.replace(/\n\s+/g, '').trim();
13 | };
14 |
15 | // Using `html` as a synonym for `oneline` in order to get syntax highlighting in editors
16 | const html = oneline;
17 |
18 | export {oneline, html};
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/transformers/element/heading.ts:
--------------------------------------------------------------------------------
1 | import {$isHeadingNode} from '@lexical/rich-text';
2 | import generateId from '../../utils/generate-id';
3 | import type {RendererOptions} from '@tryghost/kg-default-nodes';
4 | import type {ElementNode} from 'lexical';
5 | import type {ExportChildren} from '..';
6 |
7 | module.exports = {
8 | export(node: ElementNode, options: RendererOptions, exportChildren: ExportChildren) {
9 | if (!$isHeadingNode(node)) {
10 | return null;
11 | }
12 |
13 | const tag = node.getTag();
14 | const id = generateId(node.getTextContent(), options);
15 |
16 | return `<${tag} id="${id}">${exportChildren(node)}${tag}>`;
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/packages/kg-utils/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Utils
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-utils --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-utils`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/LinkInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {LinkInput} from './LinkInput';
4 |
5 | const story = {
6 | title: 'Toolbar/LinkInput',
7 | component: LinkInput,
8 | parameters: {
9 | status: {
10 | type: 'functional'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = (args) => {
17 | return (
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export const Empty = Template.bind({});
25 | Empty.args = {
26 | href: ''
27 | };
28 |
29 | export const Populated = Template.bind({});
30 | Populated.args = {
31 | href: 'https://ghost.org'
32 | };
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/utils/generate-id.ts:
--------------------------------------------------------------------------------
1 | import type {RendererOptions} from '@tryghost/kg-default-nodes';
2 | import {slugify} from '@tryghost/kg-utils';
3 |
4 | function generateId(text: string, options: RendererOptions) {
5 | if (!options.usedIdAttributes) {
6 | options.usedIdAttributes = {};
7 | }
8 |
9 | const id = slugify(text, options);
10 | let deduplicatedId = id;
11 |
12 | if (options.usedIdAttributes[id] !== undefined) {
13 | deduplicatedId += `-${options.usedIdAttributes[id]}`;
14 |
15 | options.usedIdAttributes[id] += 1;
16 | } else {
17 | options.usedIdAttributes[id] = 1;
18 | }
19 |
20 | return deduplicatedId;
21 | }
22 |
23 | export default generateId;
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/kg-converters/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Converters
2 |
3 | Functions for converting between serialized Lexical and Mobiledoc formats
4 |
5 | ## Install
6 |
7 | `npm install @tryghost/kg-converters --save`
8 |
9 | or
10 |
11 | `yarn add @tryghost/kg-converters`
12 |
13 | ## Usage
14 |
15 |
16 | ## Develop
17 |
18 | This is a monorepo package.
19 |
20 | Follow the instructions for the top-level repo.
21 | 1. `git clone` this repo & `cd` into it as usual
22 | 2. Run `yarn` to install top-level dependencies.
23 |
24 |
25 |
26 | ## Test
27 |
28 | - `yarn lint` run just eslint
29 | - `yarn test` run lint and tests
30 |
31 |
32 |
33 | # Copyright & License
34 |
35 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/utils/rgb-to-hex.js:
--------------------------------------------------------------------------------
1 | export const rgbToHex = (rgb) => {
2 | if (rgb === 'transparent') {
3 | return rgb;
4 | }
5 |
6 | try {
7 | // Extract the red, green, and blue values from the RGB string
8 | const [r, g, b] = rgb.match(/\d+/g);
9 | // Convert each component to hexadecimal
10 | const red = parseInt(r, 10).toString(16).padStart(2, '0');
11 | const green = parseInt(g, 10).toString(16).padStart(2, '0');
12 | const blue = parseInt(b, 10).toString(16).padStart(2, '0');
13 | // Concatenate the hexadecimal values
14 | const hex = `#${red}${green}${blue}`;
15 | return hex;
16 | } catch (e) {
17 | return null;
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/packages/kg-html-to-lexical/README.md:
--------------------------------------------------------------------------------
1 | # Html To Lexical
2 |
3 | Convert HTML strings into Lexical editor state objects
4 |
5 | ## Install
6 |
7 | `npm install @tryghost/kg-html-to-lexical --save`
8 |
9 | or
10 |
11 | `yarn add @tryghost/kg-html-to-lexical`
12 |
13 | ## Usage
14 |
15 |
16 | ## Develop
17 |
18 | This is a monorepo package.
19 |
20 | Follow the instructions for the top-level repo.
21 | 1. `git clone` this repo & `cd` into it as usual
22 | 2. Run `yarn` to install top-level dependencies.
23 |
24 |
25 |
26 | ## Test
27 |
28 | - `yarn lint` run just eslint
29 | - `yarn test` run lint and tests
30 |
31 |
32 |
33 | # Copyright & License
34 |
35 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
36 |
--------------------------------------------------------------------------------
/packages/kg-lexical-html-renderer/lib/get-dynamic-data-nodes.ts:
--------------------------------------------------------------------------------
1 | import {$getRoot} from 'lexical';
2 | import {$isKoenigCard, KoenigDecoratorNode} from '@tryghost/kg-default-nodes';
3 |
4 | import type {EditorState} from 'lexical';
5 |
6 | export default function getDynamicDataNodes(editorState: EditorState): KoenigDecoratorNode[] {
7 | const dynamicNodes: KoenigDecoratorNode[] = [];
8 |
9 | editorState.read(() => {
10 | const root = $getRoot();
11 | const nodes = root.getChildren();
12 |
13 | nodes.forEach((node) => {
14 | if ($isKoenigCard(node) && node.hasDynamicData?.()) {
15 | dynamicNodes.push(node);
16 | }
17 | });
18 | });
19 |
20 | return dynamicNodes;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/assets/icons/kg-dollar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/content/minimal-content.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": {
3 | "children": [
4 | {
5 | "children": [
6 | {
7 | "detail": 0,
8 | "format": 0,
9 | "mode": "normal",
10 | "style": "",
11 | "text": "There's a whole lot to discover in this editor. Let us help you settle in.",
12 | "type": "text",
13 | "version": 1
14 | }
15 | ],
16 | "direction": "ltr",
17 | "format": "",
18 | "indent": 0,
19 | "type": "paragraph",
20 | "version": 1
21 | }
22 | ],
23 | "direction": "ltr",
24 | "format": "",
25 | "indent": 0,
26 | "type": "root",
27 | "version": 1
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/kg-card-factory/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Card Factory
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-card-factory --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-card-factory`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Default Transforms
2 |
3 | Default Lexical Node transforms used across our Koenig packages
4 |
5 | ## Install
6 |
7 | `npm install @tryghost/kg-default-transforms --save`
8 |
9 | or
10 |
11 | `yarn add @tryghost/kg-default-transforms`
12 |
13 | ## Usage
14 |
15 |
16 | ## Develop
17 |
18 | This is a monorepo package.
19 |
20 | Follow the instructions for the top-level repo.
21 | 1. `git clone` this repo & `cd` into it as usual
22 | 2. Run `yarn` to install top-level dependencies.
23 |
24 |
25 |
26 | ## Test
27 |
28 | - `yarn lint` run just eslint
29 | - `yarn test` run lint and tests
30 |
31 |
32 |
33 | # Copyright & License
34 |
35 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/assets/icons/kg-card-type-image.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Default Atoms
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-default-atoms --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-default-atoms`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Default Cards
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-default-cards --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-default-cards`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/IconButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import {Tooltip} from './Tooltip';
4 |
5 | export function IconButton({className, onClick, label, dataTestId, Icon}) {
6 | return (
7 |
14 |
15 | {label && }
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/packages/kg-clean-basic-html/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Clean Basic Html
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-clean-basic-html --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-clean-basic-html`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/aside/AsideParser.js:
--------------------------------------------------------------------------------
1 | export class AsideParser {
2 | constructor(NodeClass) {
3 | this.NodeClass = NodeClass;
4 | }
5 |
6 | get DOMConversionMap() {
7 | const self = this;
8 |
9 | return {
10 | blockquote: () => ({
11 | conversion(domNode) {
12 | const isBigQuote = domNode.classList?.contains('kg-blockquote-alt');
13 | if (domNode.tagName === 'BLOCKQUOTE' && isBigQuote) {
14 | const node = new self.NodeClass();
15 | return {node};
16 | }
17 |
18 | return null;
19 | },
20 | priority: 0
21 | })
22 | };
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/lib/nodes/markdown/MarkdownNode.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable ghost/filenames/match-exported-class */
2 | import {generateDecoratorNode} from '../../generate-decorator-node';
3 | import {renderMarkdownNode} from './markdown-renderer';
4 |
5 | export class MarkdownNode extends generateDecoratorNode({
6 | nodeType: 'markdown',
7 | properties: [
8 | {name: 'markdown', default: '', urlType: 'markdown', wordCount: true}
9 | ],
10 | defaultRenderFn: renderMarkdownNode
11 | }) {
12 | isEmpty() {
13 | return !this.__markdown;
14 | }
15 | }
16 |
17 | export function $createMarkdownNode(dataset) {
18 | return new MarkdownNode(dataset);
19 | }
20 |
21 | export function $isMarkdownNode(node) {
22 | return node instanceof MarkdownNode;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/demo/components/FloatingButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const FloatingButton = ({isOpen, ...props}) => {
4 | return (
5 |
6 | props.onClick('json')}>
7 | JSON output
8 |
9 | |
10 | props.onClick('tree')}>
11 | State tree
12 |
13 |
14 | );
15 | };
16 |
17 | export default FloatingButton;
18 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/dataSrcToFile.js:
--------------------------------------------------------------------------------
1 | export async function dataSrcToFile(src, fileName) {
2 | if (!src.startsWith('data:')) {
3 | return;
4 | }
5 |
6 | const mimeType = src.split(',')[0].split(':')[1].split(';')[0];
7 |
8 | if (!fileName) {
9 | let uuid;
10 | try {
11 | uuid = window.crypto.randomUUID();
12 | } catch (e) {
13 | uuid = Math.random().toString(36).substring(2, 15);
14 | }
15 | const extension = mimeType.split('/')[1];
16 | fileName = `data-src-image-${uuid}.${extension}`;
17 | }
18 |
19 | const blob = await fetch(src).then(it => it.blob());
20 | const file = new File([blob], fileName, {type: mimeType, lastModified: new Date()});
21 |
22 | return file;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/LinkInputWithSearch.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {LinkInputWithSearch} from './LinkInputWithSearch';
4 |
5 | const story = {
6 | title: 'Toolbar/LinkInputWithSearch',
7 | component: LinkInputWithSearch,
8 | parameters: {
9 | status: {
10 | type: 'functional'
11 | }
12 | }
13 | };
14 | export default story;
15 |
16 | const Template = (args) => {
17 | return (
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export const Empty = Template.bind({});
25 | Empty.args = {
26 | href: ''
27 | };
28 |
29 | export const Populated = Template.bind({});
30 | Populated.args = {
31 | href: 'https://ghost.org'
32 | };
33 |
--------------------------------------------------------------------------------
/packages/kg-unsplash-selector/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | // Exclude Playwright test directory from Vitest runs
6 | exclude: [
7 | '**/node_modules/**',
8 | '**/dist/**',
9 | '**/test/acceptance/**' // Assuming your Playwright tests reside here
10 | ],
11 | coverage: {
12 | exclude: [
13 | '**/node_modules/**',
14 | '**/dist/**',
15 | '**/test/**',
16 | // anything that is tsx and not inside src/api
17 |
18 | '**/*.tsx'
19 | ],
20 | include: [
21 | '**/src/api/**'
22 | ]
23 | }
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/shouldIgnoreEvent.js:
--------------------------------------------------------------------------------
1 | // util to avoid processing events in Koenig when they originate from an editor
2 | // element inside a card
3 | export const shouldIgnoreEvent = (event) => {
4 | if (!event) {
5 | return false;
6 | }
7 |
8 | const {metaKey, key, target} = event;
9 | const isEscape = key === 'Escape';
10 | const isMetaEnter = metaKey && key === 'Enter';
11 |
12 | // we want to allow some keys presses to pass through as we
13 | // always override them to toggle card editing mode
14 | if (isEscape || isMetaEnter) {
15 | return false;
16 | }
17 |
18 | const isFromCardEditor = target.matches('input, textarea') || target.cmView || target.cmIgnore || !!target.closest('.cm-editor');
19 |
20 | return isFromCardEditor;
21 | };
22 |
--------------------------------------------------------------------------------
/packages/kg-markdown-html-renderer/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Markdown Html Renderer
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-markdown-html-renderer --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-markdown-html-renderer`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/kg-mobiledoc-html-renderer/README.md:
--------------------------------------------------------------------------------
1 | # Koenig Mobiledoc Html Renderer
2 |
3 | ## Install
4 |
5 | `npm install @tryghost/kg-mobiledoc-html-renderer --save`
6 |
7 | or
8 |
9 | `yarn add @tryghost/kg-mobiledoc-html-renderer`
10 |
11 |
12 | ## Usage
13 |
14 |
15 | ## Develop
16 |
17 | This is a mono repository, managed with [lerna](https://lernajs.io/).
18 |
19 | Follow the instructions for the top-level repo.
20 | 1. `git clone` this repo & `cd` into it as usual
21 | 2. Run `yarn` to install top-level dependencies.
22 |
23 |
24 | ## Run
25 |
26 | - `yarn dev`
27 |
28 |
29 | ## Test
30 |
31 | - `yarn lint` run just eslint
32 | - `yarn test` run lint and tests
33 |
34 |
35 |
36 |
37 | # Copyright & License
38 |
39 | Copyright (c) 2013-2025 Ghost Foundation - Released under the [MIT license](LICENSE).
40 |
--------------------------------------------------------------------------------
/packages/kg-default-transforms/src/transforms/merge-list-nodes.ts:
--------------------------------------------------------------------------------
1 | import {$isListNode, ListNode} from '@lexical/list';
2 | import {LexicalEditor} from 'lexical';
3 |
4 | export function mergeListNodesTransform(node: ListNode) {
5 | const nextSibling = node.getNextSibling();
6 |
7 | if ($isListNode(nextSibling) && $isListNode(node) && nextSibling.getListType() === node.getListType()) {
8 | node.append(...nextSibling.getChildren());
9 | nextSibling.remove();
10 | }
11 | }
12 |
13 | export function registerMergeListNodesTransform(editor: LexicalEditor) {
14 | if (editor.hasNodes([ListNode])) {
15 | return editor.registerNodeTransform(ListNode, mergeListNodesTransform);
16 | }
17 |
18 | // eslint-disable-next-line @typescript-eslint/no-empty-function
19 | return () => {};
20 | }
21 |
--------------------------------------------------------------------------------
/packages/kg-default-nodes/test/utils/rgb-to-hex.test.js:
--------------------------------------------------------------------------------
1 | // // import {rgbToHex} from '../../lib/utils/rgb-to-hex';
2 | const {utils} = require('../../');
3 | const rgbToHex = utils.rgbToHex;
4 |
5 | describe('rgbToHex', function () {
6 | it('should convert RGB to HEX', function () {
7 | should(rgbToHex('rgb(0, 0, 0)')).equal('#000000');
8 | should(rgbToHex('rgb(255, 255, 255)')).equal('#ffffff');
9 | should(rgbToHex('rgb(255, 0, 0)')).equal('#ff0000');
10 | should(rgbToHex('rgb(0, 255, 0)')).equal('#00ff00');
11 | should(rgbToHex('rgb(0, 0, 255)')).equal('#0000ff');
12 | should(rgbToHex('rgb(255, 255, 0)')).equal('#ffff00');
13 | });
14 |
15 | it('should handle transparent', function () {
16 | should(rgbToHex('transparent')).equal('transparent');
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/HighlightedString.jsx:
--------------------------------------------------------------------------------
1 | import escapeRegExp from 'lodash/escapeRegExp';
2 |
3 | export function HighlightedString({string, highlightString, shouldHighlight = true}) {
4 | if (!highlightString || shouldHighlight === false) {
5 | return string;
6 | }
7 |
8 | const parts = string.split(new RegExp(`(${escapeRegExp(highlightString)})`, 'gi'));
9 |
10 | return (
11 | <>
12 | {parts.map((part, index) => {
13 | if (part.toLowerCase() === highlightString.toLowerCase()) {
14 | // eslint-disable-next-line react/no-array-index-key
15 | return {part} ;
16 | }
17 |
18 | return part;
19 | })}
20 | >
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/EmojiPicker.jsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef} from 'react';
2 | import {Picker} from 'emoji-mart';
3 |
4 | export default function EmojiPicker({setInstanceRef, ...props}) {
5 | const ref = useRef(null);
6 | const instance = useRef(null);
7 |
8 | function setInstance(newInstance) {
9 | instance.current = newInstance;
10 | setInstanceRef?.(newInstance);
11 | }
12 |
13 | if (instance.current) {
14 | instance.current.update(props);
15 | }
16 |
17 | useEffect(() => {
18 | setInstance(new Picker({...props, ref}));
19 |
20 | return () => {
21 | setInstance(null);
22 | };
23 | // eslint-disable-next-line react-hooks/exhaustive-deps
24 | }, []);
25 |
26 | return React.createElement('div', {ref});
27 | }
28 |
--------------------------------------------------------------------------------
/packages/kg-default-atoms/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tryghost/kg-default-atoms",
3 | "version": "5.1.1",
4 | "repository": "https://github.com/TryGhost/Koenig/tree/master/packages/kg-default-atoms",
5 | "author": "Ghost Foundation",
6 | "license": "MIT",
7 | "main": "index.js",
8 | "scripts": {
9 | "dev": "echo \"Implement me!\"",
10 | "test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha './test/**/*.test.js'",
11 | "lint": "eslint . --ext .js --cache",
12 | "posttest": "yarn lint"
13 | },
14 | "engines": {
15 | "node": "^18.12.1 || ^20.11.1 || ^22.13.1"
16 | },
17 | "files": [
18 | "index.js",
19 | "lib"
20 | ],
21 | "publishConfig": {
22 | "access": "public"
23 | },
24 | "devDependencies": {
25 | "c8": "10.1.3",
26 | "simple-dom": "1.4.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/components/ui/file-selectors/Tenor/Error.jsx:
--------------------------------------------------------------------------------
1 | import {ERROR_TYPE} from '../../../../utils/services/tenor.js';
2 |
3 | export function Error({error}) {
4 | if (error === ERROR_TYPE.COMMON) {
5 | return (
6 |
7 | Uh-oh! Trouble reaching the Tenor API, please check your connection
8 |
9 | );
10 | }
11 |
12 | if (error === ERROR_TYPE.INVALID_API_KEY) {
13 | return (
14 |
15 | This version of the Tenor API is no longer supported. Please update your API key by following our
16 | documentation here .
17 |
18 | );
19 | }
20 | return (
21 | {error}
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/hooks/usePreviousFocus.js:
--------------------------------------------------------------------------------
1 | import {useRef} from 'react';
2 |
3 | export const usePreviousFocus = (onClick, name) => {
4 | const previousRangeRef = useRef(null);
5 |
6 | const handleMousedown = () => {
7 | const selection = document.getSelection();
8 | previousRangeRef.current = (selection.rangeCount === 0 ? null : selection.getRangeAt(0));
9 | };
10 |
11 | const handleClick = (e) => {
12 | e.preventDefault();
13 | onClick(name);
14 |
15 | if (previousRangeRef.current) {
16 | const selection = document.getSelection();
17 | selection.removeAllRanges();
18 | selection.addRange(previousRangeRef.current);
19 | previousRangeRef.current = null;
20 | }
21 | };
22 |
23 | return {handleMousedown, handleClick};
24 | };
25 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/getDOMRangeRect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | *
7 | */
8 | export function getDOMRangeRect(
9 | nativeSelection,
10 | rootElement,
11 | ) {
12 | const domRange = nativeSelection.getRangeAt(0);
13 |
14 | let rect;
15 |
16 | if (nativeSelection.anchorNode === rootElement) {
17 | let inner = rootElement;
18 | while (inner.firstElementChild != null) { // eslint-disable-line eqeqeq
19 | inner = inner.firstElementChild;
20 | }
21 | rect = inner.getBoundingClientRect();
22 | } else {
23 | rect = domRange.getBoundingClientRect();
24 | }
25 |
26 | return rect;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/kg-default-cards/test/cards/paywall.test.js:
--------------------------------------------------------------------------------
1 | // Switch these lines once there are useful utils
2 | // const testUtils = require('./utils');
3 | require('../utils');
4 |
5 | const card = require('../../lib/cards/paywall');
6 | const SimpleDom = require('simple-dom');
7 | const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
8 |
9 | describe('paywall card', function () {
10 | it('has correct properties', function () {
11 | card.name.should.eql('paywall');
12 | card.type.should.eql('dom');
13 | });
14 |
15 | it('generates a members-only comment', function () {
16 | let opts = {
17 | env: {
18 | dom: new SimpleDom.Document()
19 | }
20 | };
21 |
22 | serializer.serialize(card.render(opts)).should.match('');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/packages/kg-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tryghost/kg-utils",
3 | "version": "1.0.34",
4 | "repository": "https://github.com/TryGhost/Koenig/tree/main/packages/kg-utils",
5 | "author": "Ghost Foundation",
6 | "license": "MIT",
7 | "main": "index.js",
8 | "scripts": {
9 | "dev": "echo \"Implement me!\"",
10 | "test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha './test/**/*.test.js'",
11 | "lint": "eslint . --ext .js --cache",
12 | "posttest": "yarn lint"
13 | },
14 | "files": [
15 | "index.js",
16 | "lib"
17 | ],
18 | "publishConfig": {
19 | "access": "public"
20 | },
21 | "devDependencies": {
22 | "c8": "10.1.3",
23 | "mocha": "11.7.5",
24 | "should": "13.2.3",
25 | "sinon": "21.0.0"
26 | },
27 | "dependencies": {
28 | "semver": "^7.7.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/koenig-lexical/src/utils/sanitize-html.js:
--------------------------------------------------------------------------------
1 | import DOMPurify from 'dompurify';
2 |
3 | export function sanitizeHtml(html = '', options = {}) {
4 | options = {
5 | ...{replaceJS: true},
6 | ...options
7 | };
8 |
9 | // replace script and iFrame
10 | if (options.replaceJS) {
11 | html = html.replace(/