{"_id":"@bwthomas/outline-numbering","_rev":"4-d1c6010ed02de160d8f876e971e3d207","name":"@bwthomas/outline-numbering","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@bwthomas/outline-numbering","version":"0.1.0","keywords":["prosemirror","prosemirror-plugin","tiptap","tiptap-extension","outline","numbering","ordered-list","alphanumeric","decimal"],"author":{"name":"Blake Thomas","email":"bwthomas@users.noreply.github.com"},"license":"MIT","_id":"@bwthomas/outline-numbering@0.1.0","maintainers":[{"name":"bwthomas","email":"bwthomas@gmail.com"}],"homepage":"https://github.com/bwthomas/outline-numbering#readme","bugs":{"url":"https://github.com/bwthomas/outline-numbering/issues"},"dist":{"shasum":"7d5891f802ad14689d7f900aff23ff9f68e04338","tarball":"https://registry.npmjs.org/@bwthomas/outline-numbering/-/outline-numbering-0.1.0.tgz","fileCount":17,"integrity":"sha512-gODgcDh4RNbSzidFGf1l3kHhiteiafNGOpImdwmBTqWqGAS5KJa6cMffb1DGJRN0JEIPS7CcYnAqdI8K6vOwTg==","signatures":[{"sig":"MEUCIQCK7IQ0lUnRAwiM6MODaIhXU8l+1fXWl5aRrQOvfL/DwgIgJQRZo41nucCX7vWcBdscoSOfXOuF+PIJzIgcRmpMreE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":98883},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tiptap":{"types":"./dist/tiptap.d.ts","import":"./dist/tiptap.js","require":"./dist/tiptap.cjs"}},"gitHead":"9e9477f660ba39dbb7ec470b2329e57eaae8480c","scripts":{"ci":"npm run typecheck && npm run test && npm run build","test":"vitest run","build":"tsup","prepare":"npm run build","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"bwthomas","email":"bwthomas@gmail.com"},"repository":{"url":"git+https://github.com/bwthomas/outline-numbering.git","type":"git"},"_npmVersion":"10.2.4","description":"Framework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies: alphanumeric (Harvard / Purdue OWL I/A/1/a), decimal, legal, arabic, lower-roman, or custom.","directories":{},"_nodeVersion":"18.19.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"^5.9.0","@tiptap/core":"^3.22.3","prosemirror-view":"^1.40.0","prosemirror-model":"^1.25.0","prosemirror-state":"^1.4.0"},"peerDependencies":{"@tiptap/core":"^3.0.0","prosemirror-view":"^1.0.0","prosemirror-state":"^1.0.0"},"peerDependenciesMeta":{"@tiptap/core":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/outline-numbering_0.1.0_1776350494591_0.36270657162561126","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bwthomas/outline-numbering","version":"0.2.0","keywords":["prosemirror","prosemirror-plugin","tiptap","tiptap-extension","outline","numbering","ordered-list","alphanumeric","decimal"],"author":{"name":"Blake Thomas","email":"bwthomas@users.noreply.github.com"},"license":"MIT","_id":"@bwthomas/outline-numbering@0.2.0","maintainers":[{"name":"bwthomas","email":"bwthomas@gmail.com"}],"homepage":"https://github.com/bwthomas/outline-numbering#readme","bugs":{"url":"https://github.com/bwthomas/outline-numbering/issues"},"dist":{"shasum":"1fe42e6e5164b769f910e99e6b3ebc79a9dc44b8","tarball":"https://registry.npmjs.org/@bwthomas/outline-numbering/-/outline-numbering-0.2.0.tgz","fileCount":17,"integrity":"sha512-OZtbf5XYlMpZ+7Sg20xm8Xs/CWZWb/i02Z7dQm3UwLBfm9yd7Cwmr70M0UHcNrFlrqILmcIrQjBhSRT66IkfsA==","signatures":[{"sig":"MEUCIFio5mQA8nBfV+blWeVvqSZgoR7+dPFW6zQCbk0WOV4eAiEA6Lgq56xqxHzT4SbsaEjiINLCEP7TTnyq91rqOLzsDns=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112523},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tiptap":{"types":"./dist/tiptap.d.ts","import":"./dist/tiptap.js","require":"./dist/tiptap.cjs"}},"gitHead":"6cc9b59e1f36b9a96bd3dbbe457e83260adb68c0","scripts":{"ci":"npm run typecheck && npm run test && npm run build","test":"vitest run","build":"tsup","prepare":"npm run build","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"bwthomas","email":"bwthomas@gmail.com"},"repository":{"url":"git+https://github.com/bwthomas/outline-numbering.git","type":"git"},"_npmVersion":"11.12.1","description":"Framework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies: alphanumeric (Harvard / Purdue OWL I/A/1/a), decimal, legal, arabic, lower-roman, or custom.","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"^5.9.0","@tiptap/core":"^3.22.3","prosemirror-view":"^1.40.0","prosemirror-model":"^1.25.0","prosemirror-state":"^1.4.0"},"peerDependencies":{"@tiptap/core":"^3.0.0","prosemirror-view":"^1.0.0","prosemirror-state":"^1.0.0"},"peerDependenciesMeta":{"@tiptap/core":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/outline-numbering_0.2.0_1777255846186_0.3510396494938679","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@bwthomas/outline-numbering","version":"0.3.0","keywords":["prosemirror","prosemirror-plugin","tiptap","tiptap-extension","outline","numbering","ordered-list","alphanumeric","decimal"],"author":{"name":"Blake Thomas","email":"bwthomas@users.noreply.github.com"},"license":"MIT","_id":"@bwthomas/outline-numbering@0.3.0","maintainers":[{"name":"bwthomas","email":"bwthomas@gmail.com"}],"homepage":"https://github.com/bwthomas/outline-numbering#readme","bugs":{"url":"https://github.com/bwthomas/outline-numbering/issues"},"dist":{"shasum":"17a6b49cec24118dacdd5d927ba40e12c4b49c6b","tarball":"https://registry.npmjs.org/@bwthomas/outline-numbering/-/outline-numbering-0.3.0.tgz","fileCount":17,"integrity":"sha512-Yxl/ZDevcdApNMzpf8lhoMg+BbqevXKsx+nVflIEy1/ODOZuVl7xQ62eu9sJkcMY5T9auLNVWK5o6Cs4r/lX5A==","signatures":[{"sig":"MEQCIGhsZlq5orMBi6eOtWt6wstvjDv4wYQXB12F6rComs6WAiBCtWX2NMNy3V24IXGvNQnXRFSQRr82CC08hRhCb+PMww==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":127731},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tiptap":{"types":"./dist/tiptap.d.ts","import":"./dist/tiptap.js","require":"./dist/tiptap.cjs"}},"gitHead":"246939b86a1e07d021ab7be62bb9e2fafc5cc1f2","scripts":{"ci":"npm run typecheck && npm run test && npm run build","test":"vitest run","build":"tsup","prepare":"npm run build","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"bwthomas","email":"bwthomas@gmail.com"},"repository":{"url":"git+https://github.com/bwthomas/outline-numbering.git","type":"git"},"_npmVersion":"10.2.4","description":"Framework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies: alphanumeric (Harvard / Purdue OWL I/A/1/a), decimal, legal, arabic, lower-roman, or custom.","directories":{},"_nodeVersion":"18.19.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","typescript":"^5.9.0","@tiptap/core":"^3.22.3","prosemirror-view":"^1.40.0","prosemirror-model":"^1.25.0","prosemirror-state":"^1.4.0"},"peerDependencies":{"@tiptap/core":"^3.0.0","prosemirror-view":"^1.0.0","prosemirror-state":"^1.0.0"},"peerDependenciesMeta":{"@tiptap/core":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/outline-numbering_0.3.0_1777410356318_0.4792044253678105","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@bwthomas/outline-numbering","version":"0.4.0","description":"Framework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies: alphanumeric (Harvard / Purdue OWL I/A/1/a), decimal, legal, arabic, lower-roman, or custom.","license":"MIT","author":{"name":"Blake Thomas","email":"bwthomas@users.noreply.github.com"},"repository":{"type":"git","url":"git+https://github.com/bwthomas/outline-numbering.git"},"homepage":"https://github.com/bwthomas/outline-numbering#readme","bugs":{"url":"https://github.com/bwthomas/outline-numbering/issues"},"keywords":["prosemirror","prosemirror-plugin","tiptap","tiptap-extension","outline","numbering","ordered-list","alphanumeric","decimal"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tiptap":{"types":"./dist/tiptap.d.ts","import":"./dist/tiptap.js","require":"./dist/tiptap.cjs"}},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","ci":"npm run typecheck && npm run test && npm run build","prepare":"npm run build"},"peerDependencies":{"@tiptap/core":"^3.0.0","prosemirror-state":"^1.0.0","prosemirror-view":"^1.0.0"},"peerDependenciesMeta":{"@tiptap/core":{"optional":true}},"devDependencies":{"@tiptap/core":"^3.22.3","prosemirror-model":"^1.25.0","prosemirror-state":"^1.4.0","prosemirror-view":"^1.40.0","tsup":"^8.5.0","typescript":"^5.9.0","vitest":"^3.2.0"},"gitHead":"73290925f65ea2c3aa9d482c9513a8dd3c20901b","_id":"@bwthomas/outline-numbering@0.4.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-grZciZQ1uJl+Oi/3L8xCqbg0Ig9znSmO/4XMZmX8Kqyzrh2tAXASuF6hBPKN13z5l+UafCvYUkD+fo9oNPzNUw==","shasum":"faeefab5752fd2f839c246fd0e671725a084f497","tarball":"https://registry.npmjs.org/@bwthomas/outline-numbering/-/outline-numbering-0.4.0.tgz","fileCount":17,"unpackedSize":166915,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGXwugrb8nM/nfXIfA3eLQa+60laMsb5V2s5SWfihpaCAiEAlE/uBwIAWRd00OsT6w4tA0ORa4tSBq5uI/0jG29JGDc="}]},"_npmUser":{"name":"bwthomas","email":"bwthomas@gmail.com"},"directories":{},"maintainers":[{"name":"bwthomas","email":"bwthomas@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/outline-numbering_0.4.0_1788286306296_0.8523161098013754"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-16T14:41:34.460Z","modified":"2026-09-01T18:11:46.647Z","0.1.0":"2026-04-16T14:41:34.740Z","0.2.0":"2026-04-27T02:10:46.336Z","0.3.0":"2026-04-28T21:05:56.458Z","0.4.0":"2026-09-01T18:11:46.470Z"},"bugs":{"url":"https://github.com/bwthomas/outline-numbering/issues"},"author":{"name":"Blake Thomas","email":"bwthomas@users.noreply.github.com"},"license":"MIT","homepage":"https://github.com/bwthomas/outline-numbering#readme","keywords":["prosemirror","prosemirror-plugin","tiptap","tiptap-extension","outline","numbering","ordered-list","alphanumeric","decimal"],"repository":{"type":"git","url":"git+https://github.com/bwthomas/outline-numbering.git"},"description":"Framework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies: alphanumeric (Harvard / Purdue OWL I/A/1/a), decimal, legal, arabic, lower-roman, or custom.","maintainers":[{"name":"bwthomas","email":"bwthomas@gmail.com"}],"readme":"# @bwthomas/outline-numbering\n\nFramework-agnostic ProseMirror plugin (with Tiptap wrapper) for nested ordered-list numbering with pluggable strategies.\n\n- **Alphanumeric** (Harvard / Purdue OWL) — `I. A. 1. a.`\n- **Decimal** — `1. 2. 3.` at every depth (pair with CSS counters for `1.1.1.` composition)\n- **Legal** — `I. A. 1. a. (1) (a) (i)` across 7 levels\n- **Arabic flat** — plain numbered lists\n- **Lower Roman flat** — `i. ii. iii.`\n- **Custom** — any `(counter, depth) → string` you like (emoji, CJK, whatever)\n\n## Install\n\n```sh\nnpm install @bwthomas/outline-numbering\n```\n\nPeer deps: `prosemirror-state`, `prosemirror-view`. `@tiptap/core` is required only if you use the Tiptap wrapper.\n\n## Usage\n\n### Tiptap\n\n```ts\nimport { Editor } from '@tiptap/core';\nimport StarterKit from '@tiptap/starter-kit';\nimport { OutlineNumbering } from '@bwthomas/outline-numbering/tiptap';\nimport { decimalStrategy } from '@bwthomas/outline-numbering';\n\nnew Editor({\n  extensions: [\n    StarterKit,\n    OutlineNumbering.configure({ strategy: decimalStrategy }),\n  ],\n});\n```\n\n### Raw ProseMirror\n\n```ts\nimport { EditorState } from 'prosemirror-state';\nimport { outlineNumberingPlugin, alphanumericStrategy } from '@bwthomas/outline-numbering';\n\nconst state = EditorState.create({\n  doc: myDoc,\n  plugins: [\n    outlineNumberingPlugin({ strategy: alphanumericStrategy }),\n    ...otherPlugins,\n  ],\n});\n```\n\n### CSS\n\nThe plugin only attaches data attributes. Style the markers yourself:\n\n```css\nol li[data-outline-marker] {\n  position: relative;\n  padding-left: 2.5em;\n  list-style: none;\n}\n\nol li[data-outline-marker]::before {\n  content: attr(data-outline-marker);\n  position: absolute;\n  left: 0;\n  width: 2em;\n  text-align: right;\n  opacity: 0.8;\n}\n```\n\n## Strategies\n\n```ts\nimport {\n  alphanumericStrategy,  // I. A. 1. a. — the default\n  harvardStrategy,       // alias for alphanumericStrategy\n  purdueOwlStrategy,     // alias for alphanumericStrategy\n  decimalStrategy,       // 1. at every depth\n  legalStrategy,         // I. A. 1. a. (1) (a) (i) over 7 levels\n  arabicStrategy,        // 1. at every depth\n  lowerRomanStrategy,    // i. at every depth\n} from '@bwthomas/outline-numbering';\n```\n\n### Custom strategies\n\n```ts\nimport type { OutlineNumberingStrategy } from '@bwthomas/outline-numbering';\n\nconst emojiStrategy: OutlineNumberingStrategy = {\n  name: 'emoji',\n  format(counter, depth) {\n    const glyphs = ['🎯', '✦', '🔹', '▸'];\n    return `${glyphs[depth % glyphs.length]} `;\n  },\n};\n```\n\nThe `format` function must be pure. It's called once per list item during every walk of the document.\n\n## Options\n\n```ts\noutlineNumberingPlugin({\n  strategy: alphanumericStrategy,\n  descendThroughContainers: true,\n});\n```\n\n- **`strategy`** — any `OutlineNumberingStrategy`. Default: `alphanumericStrategy`.\n- **`descendThroughContainers`** — when `true` (default), ordered lists nested inside non-ordered containers (bullet lists, blockquotes, custom blocks) within a list item are discovered and numbered one level deeper than the enclosing item. When `false`, only ordered lists that are direct children of a list item are walked.\n- **`skipItem`** — `(item, {depth, pos, index}) => boolean`. A row this returns `true` for is passed over entirely: it takes no marker **and does not advance the counter**, so the row after it gets the number this one would have had. It keeps its `data-outline-depth`, and its children are still walked, so a nested list inside a skipped row still numbers. Default: `skipCardOnlyItems`.\n\n### `skipCardOnlyItems` — the default, and why it changed in 0.4.0\n\n**Breaking:** before 0.4.0 every `listItem` took a number. The default now passes over a row that holds no words of its own but does hold another, non-list block — a citation card, a callout, an embed. Such a row is a *container* a reader does not count, so numbering it makes the outline's numbering disagree with what the reader sees. Pass `numberEveryItem` as `skipItem` to restore the old behaviour exactly.\n\nThe test is on the row's **leading child**, not on \"does the row contain any text\", and that distinction is load-bearing. A citation-style node typically declares `content: 'text*'`, so it *is* a textblock carrying text — the very shape a \"row with no words\" test reads as words. Since `listItem` content is `paragraph block*`, a row's first child is always its own structural paragraph, and whether *that* is empty is what separates a row someone wrote in from a row that is only a container.\n\n```\nlistItem(paragraph(\"Methods\"))              → numbered. Has words.\nlistItem(paragraph(\"\"))                     → numbered. An empty row someone is\n                                              about to type into must keep its\n                                              number, or it appears on keypress.\nlistItem(paragraph(\"\"), citation(…))        → SKIPPED. A card-only row.\nlistItem(paragraph(\"Methods\"), citation(…)) → numbered. The card is beside words.\nlistItem(paragraph(\"\"), orderedList(…))     → numbered. A row that only groups a\n                                              sublist is still a row.\n```\n\n## API\n\n```ts\n// Framework-agnostic\nimport {\n  outlineNumberingPlugin,   // (options?) => Plugin<DecorationSet>\n  computeDecorations,       // (doc, strategy, descendThroughContainers?) => DecorationSet\n  OUTLINE_NUMBERING_KEY,    // PluginKey — use to read plugin state from other plugins\n  OutlineNumberingStrategy,\n  OutlineNumberingPluginOptions,\n  OutlineNumberingItemInfo,\n  skipCardOnlyItems,        // default skipItem predicate\n  numberEveryItem,          // pre-0.4 behaviour: every row numbers\n  toRoman,\n  toLetter,\n  // ...all named strategies\n} from '@bwthomas/outline-numbering';\n\n// Tiptap wrapper\nimport { OutlineNumbering, OutlineNumberingOptions } from '@bwthomas/outline-numbering/tiptap';\n```\n\n## Things to keep in mind if you fork this\n\nShort list of conventions worth preserving if you take this in a different direction:\n\n- **Support both raw ProseMirror and Tiptap from a single package.** The main entry (`./`) is framework-neutral; the Tiptap wrapper lives at the `/tiptap` subpath. `@tiptap/core` is declared as an optional peer via `peerDependenciesMeta`, so raw-PM users don't get pulled into the Tiptap dep tree. Keep this split if you keep the repo; drop it only if you deliberately commit to one framework.\n- **Strategies are pure.** `format(counter, depth) → string`, same inputs → same output, no closures over mutable state. The plugin invokes them during a walk and assumes no side effects.\n- **Keep the plugin render-only.** It attaches data attributes; it never manipulates the document or dispatches transactions. That's what keeps it composable with collaborative editing (Yjs) and undo/redo — the walker runs against the current doc and produces decorations, full stop.\n- **Alphanumeric is the canonical name; `harvardStrategy` / `purdueOwlStrategy` are aliases.** If you add more, prefer the technical/neutral name as primary and informal names as aliases.\n- **Future: consider wrappers for Lexical, Slate, other editor frameworks.** The walker is framework-agnostic in principle — only the plugin lifecycle differs.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n","readmeFilename":"README.md"}