From 2e285982433c623b0f31d21d45092f98f5690702 Mon Sep 17 00:00:00 2001 From: gsb Date: Wed, 29 Apr 2026 07:48:06 +0000 Subject: [PATCH] =?UTF-8?q?Two=20build=20targets=20=E2=80=94=20core=20and?= =?UTF-8?q?=20full?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dist/ribbit/ribbit-core.js — editor without extra features dist/ribbit/ribbit.js — editor with all features Added a theme feature flag for vim keybindings --- package.json | 4 +++- src/ts/ribbit-core.ts | 21 +++++++++++++++++++++ src/ts/ribbit-editor.ts | 30 ++++++++++++++++-------------- src/ts/types.ts | 1 + test/vim.test.ts | 14 +++++++------- 5 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 src/ts/ribbit-core.ts diff --git a/package.json b/package.json index 38efa6a..3a5fa27 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "dist/ribbit/" ], "scripts": { - "build": "mkdir -p dist/ribbit && npm run build:check && npm run build:js && npm run build:min && npm run build:css", + "build": "mkdir -p dist/ribbit && npm run build:check && npm run build:js && npm run build:min && npm run build:core && npm run build:core-min && npm run build:css", "build:check": "tsc --noEmit", "build:js": "esbuild src/ts/ribbit-editor.ts --bundle --format=iife --global-name=ribbit --sourcemap --outfile=dist/ribbit/ribbit.js", "build:min": "esbuild src/ts/ribbit-editor.ts --bundle --format=iife --global-name=ribbit --minify --outfile=dist/ribbit/ribbit.min.js", + "build:core": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --sourcemap --outfile=dist/ribbit/ribbit-core.js", + "build:core-min": "esbuild src/ts/ribbit-core.ts --bundle --format=iife --global-name=ribbit --minify --outfile=dist/ribbit/ribbit-core.min.js", "build:css": "cp src/static/ribbit-core.css dist/ribbit/ && cp -r src/static/themes dist/ribbit/", "test": "npm run build && jest --verbose", "test:coverage": "npm run build && jest --coverage" diff --git a/src/ts/ribbit-core.ts b/src/ts/ribbit-core.ts new file mode 100644 index 0000000..5842937 --- /dev/null +++ b/src/ts/ribbit-core.ts @@ -0,0 +1,21 @@ +/* + * ribbit-core.ts — lightweight entry point without optional features (vim). + * + * Same API as ribbit-editor.ts but excludes VimHandler. + */ + +import { HopDown } from './hopdown'; +import { defaultTags, defaultBlockTags, defaultInlineTags, inlineTag } from './tags'; +import { defaultTheme } from './default-theme'; +import { Ribbit, camelCase, decodeHtmlEntities, encodeHtmlEntities } from './ribbit'; +import { type MacroDef } from './macros'; + +export { RibbitEditor as Editor } from './ribbit-editor'; +export { Ribbit as Viewer }; +export { HopDown }; +export { inlineTag }; +export { defaultTags, defaultBlockTags, defaultInlineTags }; +export { defaultTheme }; +export { camelCase, decodeHtmlEntities, encodeHtmlEntities }; +export { ToolbarManager } from './toolbar'; +export type { MacroDef }; diff --git a/src/ts/ribbit-editor.ts b/src/ts/ribbit-editor.ts index dfaa3f8..b9ac646 100644 --- a/src/ts/ribbit-editor.ts +++ b/src/ts/ribbit-editor.ts @@ -23,7 +23,7 @@ import { type MacroDef } from './macros'; * editor.view(); // switch to read-only view */ export class RibbitEditor extends Ribbit { - private vim!: VimHandler; + private vim?: VimHandler; run(): void { this.states = { @@ -32,17 +32,19 @@ export class RibbitEditor extends Ribbit { WYSIWYG: 'wysiwyg' }; - this.vim = new VimHandler((mode) => { - if (mode === 'normal') { - this.toolbar.disable(); - this.element.classList.add('vim-normal'); - this.element.classList.remove('vim-insert'); - } else { - this.toolbar.enable(); - this.element.classList.add('vim-insert'); - this.element.classList.remove('vim-normal'); - } - }); + if (this.theme.features?.vim) { + this.vim = new VimHandler((mode) => { + if (mode === 'normal') { + this.toolbar.disable(); + this.element.classList.add('vim-normal'); + this.element.classList.remove('vim-insert'); + } else { + this.toolbar.enable(); + this.element.classList.add('vim-insert'); + this.element.classList.remove('vim-normal'); + } + }); + } this.#bindEvents(); this.element.classList.add('loaded'); @@ -218,7 +220,7 @@ export class RibbitEditor extends Ribbit { wysiwyg(): void { if (this.getState() === this.states.WYSIWYG) return; - this.vim.detach(); + this.vim?.detach(); this.element.contentEditable = 'true'; this.element.innerHTML = this.getHTML(); Array.from(this.element.querySelectorAll('.macro')).forEach(el => { @@ -238,7 +240,7 @@ export class RibbitEditor extends Ribbit { if (this.state === this.states.EDIT) return; this.element.contentEditable = 'true'; this.element.innerHTML = encodeHtmlEntities(this.getMarkdown()); - this.vim.attach(this.element); + this.vim?.attach(this.element); this.setState(this.states.EDIT); } diff --git a/src/ts/types.ts b/src/ts/types.ts index 9339a3b..2066155 100644 --- a/src/ts/types.ts +++ b/src/ts/types.ts @@ -68,6 +68,7 @@ export interface InlineTagDef { export interface RibbitThemeFeatures { sourceMode?: boolean; + vim?: boolean; } /** diff --git a/test/vim.test.ts b/test/vim.test.ts index 90755e9..35456ca 100644 --- a/test/vim.test.ts +++ b/test/vim.test.ts @@ -6,14 +6,14 @@ describe('VimHandler', () => { beforeEach(() => resetDOM('hello world')); it('starts in insert mode', () => { - const editor = new r.Editor({}); + const editor = new r.Editor({ currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.edit(); expect(editor.element.classList.contains('vim-insert')).toBe(true); }); it('Esc enters normal mode', () => { - const editor = new r.Editor({}); + const editor = new r.Editor({ currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.edit(); editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' })); @@ -22,7 +22,7 @@ describe('VimHandler', () => { }); it('i returns to insert mode', () => { - const editor = new r.Editor({}); + const editor = new r.Editor({ currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.edit(); // Enter normal mode @@ -34,7 +34,7 @@ describe('VimHandler', () => { }); it('disables toolbar in normal mode', () => { - const editor = new r.Editor({ autoToolbar: false }); + const editor = new r.Editor({ autoToolbar: false, currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.toolbar.render(); editor.edit(); @@ -45,7 +45,7 @@ describe('VimHandler', () => { }); it('re-enables toolbar in insert mode', () => { - const editor = new r.Editor({ autoToolbar: false }); + const editor = new r.Editor({ autoToolbar: false, currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.toolbar.render(); editor.edit(); @@ -56,7 +56,7 @@ describe('VimHandler', () => { }); it('detaches when leaving edit mode', () => { - const editor = new r.Editor({}); + const editor = new r.Editor({ currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.edit(); editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' })); @@ -68,7 +68,7 @@ describe('VimHandler', () => { }); it('only activates in edit mode', () => { - const editor = new r.Editor({}); + const editor = new r.Editor({ currentTheme: 'vim', themes: [{ name: 'vim', features: { sourceMode: true, vim: true }, tags: r.defaultTags }] }); editor.run(); editor.wysiwyg(); // Esc in wysiwyg should not add vim classes