@sethumadhavan004/ink-editor 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2224,6 +2224,103 @@ var TabIndent = Extension.create({
2224
2224
  }
2225
2225
  });
2226
2226
 
2227
+ // src/extensions/SinglePageOverflow.ts
2228
+ import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
2229
+ var pluginKey2 = new PluginKey3("singlePageOverflow");
2230
+ function getBodyHeightPx2(view, pageSize) {
2231
+ const card = view.dom.closest(".ink-page-card");
2232
+ if (card) {
2233
+ const style = window.getComputedStyle(card);
2234
+ const paddingTop = parseFloat(style.paddingTop);
2235
+ const paddingBottom = parseFloat(style.paddingBottom);
2236
+ const totalHeight = card.offsetHeight || 0;
2237
+ const bodyPx = totalHeight - paddingTop - paddingBottom;
2238
+ if (bodyPx > 0) return Math.floor(bodyPx / 28) * 28;
2239
+ }
2240
+ const d = PAGE_DIMENSIONS[pageSize];
2241
+ const PX_PER_MM2 = 3.7795;
2242
+ return Math.floor((d.heightMm - d.paddingTopMm - d.paddingBottomMm) * PX_PER_MM2 / 28) * 28;
2243
+ }
2244
+ function splitAtOverflow(doc, view, bodyHeightPx) {
2245
+ let baseY = null;
2246
+ let splitPos = null;
2247
+ doc.forEach((node, offset) => {
2248
+ if (splitPos !== null) return;
2249
+ const pos = offset + 1;
2250
+ if (pos >= doc.content.size) return;
2251
+ let bottom;
2252
+ try {
2253
+ const coords = view.coordsAtPos(pos);
2254
+ if (baseY === null) baseY = coords.top;
2255
+ const endPos = offset + node.nodeSize - 1;
2256
+ bottom = endPos > pos ? view.coordsAtPos(endPos).bottom : coords.bottom;
2257
+ } catch {
2258
+ return;
2259
+ }
2260
+ const relBottom = bottom - (baseY ?? bottom);
2261
+ if (relBottom > bodyHeightPx) {
2262
+ splitPos = offset;
2263
+ }
2264
+ });
2265
+ if (splitPos === null) return null;
2266
+ const schema = doc.type.schema;
2267
+ const fitsContent = doc.slice(0, splitPos).content;
2268
+ const overflowContent = doc.slice(splitPos).content;
2269
+ const fitsJson = schema.nodeFromJSON({ type: "doc", content: fitsContent.toJSON() ?? [] }).toJSON();
2270
+ const overflowJson = schema.nodeFromJSON({ type: "doc", content: overflowContent.toJSON() ?? [] }).toJSON();
2271
+ return { fitsJson, overflowJson };
2272
+ }
2273
+ var SinglePageOverflow = Extension.create({
2274
+ name: "singlePageOverflow",
2275
+ addOptions() {
2276
+ return {
2277
+ pageSize: "A4",
2278
+ onOverflow: () => {
2279
+ }
2280
+ };
2281
+ },
2282
+ addProseMirrorPlugins() {
2283
+ const { pageSize, onOverflow } = this.options;
2284
+ let rafId = null;
2285
+ function scheduleCheck(view) {
2286
+ if (rafId !== null) cancelAnimationFrame(rafId);
2287
+ rafId = requestAnimationFrame(() => {
2288
+ rafId = null;
2289
+ if (view.isDestroyed) return;
2290
+ const bodyHeightPx = getBodyHeightPx2(view, pageSize);
2291
+ const result = splitAtOverflow(view.state.doc, view, bodyHeightPx);
2292
+ if (!result) return;
2293
+ const { fitsJson, overflowJson } = result;
2294
+ const schema = view.state.schema;
2295
+ const fitsDoc = schema.nodeFromJSON(fitsJson);
2296
+ const tr = view.state.tr.replaceWith(0, view.state.doc.content.size, fitsDoc.content);
2297
+ tr.setMeta("addToHistory", false);
2298
+ tr.setMeta("singlePageTrim", true);
2299
+ view.dispatch(tr);
2300
+ onOverflow(fitsJson, overflowJson);
2301
+ });
2302
+ }
2303
+ return [
2304
+ new Plugin3({
2305
+ key: pluginKey2,
2306
+ view(editorView) {
2307
+ return {
2308
+ update(view, prevState) {
2309
+ if (view.state.doc.eq(prevState.doc) || view.state.tr?.getMeta?.("singlePageTrim")) return;
2310
+ if (view.state.doc.content.size !== prevState.doc.content.size || !view.state.doc.eq(prevState.doc)) {
2311
+ scheduleCheck(view);
2312
+ }
2313
+ },
2314
+ destroy() {
2315
+ if (rafId !== null) cancelAnimationFrame(rafId);
2316
+ }
2317
+ };
2318
+ }
2319
+ })
2320
+ ];
2321
+ }
2322
+ });
2323
+
2227
2324
  // src/components/PagedEditorContent.tsx
2228
2325
  import { EditorContent } from "@tiptap/react";
2229
2326
 
@@ -2243,7 +2340,8 @@ import {
2243
2340
  ListOrdered,
2244
2341
  Indent,
2245
2342
  Outdent,
2246
- Rows
2343
+ Rows,
2344
+ FilePlus
2247
2345
  } from "lucide-react";
2248
2346
 
2249
2347
  // src/components/FontPicker.tsx
@@ -2376,7 +2474,7 @@ function ColorPanel({ colors, onChange, open, onToggle, onClose }) {
2376
2474
  }
2377
2475
 
2378
2476
  // src/components/FloatingToolbar.tsx
2379
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2477
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2380
2478
  function Sep() {
2381
2479
  return /* @__PURE__ */ jsx3("span", { className: "ink-toolbar-sep", "aria-hidden": "true" });
2382
2480
  }
@@ -2388,7 +2486,9 @@ function FloatingToolbar({
2388
2486
  font,
2389
2487
  onFontChange,
2390
2488
  colors,
2391
- onColorsChange
2489
+ onColorsChange,
2490
+ toolbarStart,
2491
+ toolbarEnd
2392
2492
  }) {
2393
2493
  const [openPanel, setOpenPanel] = useState(null);
2394
2494
  useEffect3(() => {
@@ -2479,10 +2579,26 @@ function FloatingToolbar({
2479
2579
  isActive: () => ruled,
2480
2580
  action: onToggleRuled
2481
2581
  }]
2582
+ },
2583
+ {
2584
+ key: "addpage",
2585
+ configs: [{
2586
+ label: "Add page",
2587
+ icon: /* @__PURE__ */ jsx3(FilePlus, { size: iconSize }),
2588
+ isActive: () => false,
2589
+ action: () => {
2590
+ const end = editor.state.doc.content.size - 1;
2591
+ editor.chain().focus().insertContentAt(end, [{ type: "paragraph" }, { type: "paragraph" }]).run();
2592
+ }
2593
+ }]
2482
2594
  }
2483
2595
  ];
2484
2596
  const activeGroups = allGroups.filter((g) => buttons.includes(g.key));
2485
2597
  return /* @__PURE__ */ jsx3("div", { className: "ink-floating-toolbar-wrap", children: /* @__PURE__ */ jsxs3("div", { className: "ink-floating-toolbar", role: "toolbar", "aria-label": "Text formatting", children: [
2598
+ toolbarStart && toolbarStart.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
2599
+ toolbarStart.map((node, i) => /* @__PURE__ */ jsx3("span", { style: { display: "contents" }, children: node }, i)),
2600
+ /* @__PURE__ */ jsx3(Sep, {})
2601
+ ] }),
2486
2602
  activeGroups.map((group, gi) => /* @__PURE__ */ jsxs3("span", { className: "ink-toolbar-group", style: { display: "contents" }, children: [
2487
2603
  group.configs.map((cfg) => /* @__PURE__ */ jsx3(
2488
2604
  "button",
@@ -2518,7 +2634,11 @@ function FloatingToolbar({
2518
2634
  onToggle: () => togglePanel("color"),
2519
2635
  onClose: () => setOpenPanel(null)
2520
2636
  }
2521
- )
2637
+ ),
2638
+ toolbarEnd && toolbarEnd.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
2639
+ /* @__PURE__ */ jsx3(Sep, {}),
2640
+ toolbarEnd.map((node, i) => /* @__PURE__ */ jsx3("span", { style: { display: "contents" }, children: node }, i))
2641
+ ] })
2522
2642
  ] }) });
2523
2643
  }
2524
2644
 
@@ -2534,7 +2654,10 @@ function PagedEditorContent({
2534
2654
  font,
2535
2655
  onFontChange,
2536
2656
  colors,
2537
- onColorsChange
2657
+ onColorsChange,
2658
+ toolbarStart,
2659
+ toolbarEnd,
2660
+ singlePage = false
2538
2661
  }) {
2539
2662
  const widthPx = getPageWidthPx(pageSize);
2540
2663
  const bodyWidthPx = getBodyWidthPx(pageSize);
@@ -2545,7 +2668,7 @@ function PagedEditorContent({
2545
2668
  return /* @__PURE__ */ jsxs4(
2546
2669
  "div",
2547
2670
  {
2548
- className: "ink-page-wrap",
2671
+ className: `ink-page-wrap${singlePage ? " ink-page-wrap--single" : ""}`,
2549
2672
  "data-theme": theme,
2550
2673
  style: {
2551
2674
  "--ink-bg": colors.canvasBg,
@@ -2566,7 +2689,9 @@ function PagedEditorContent({
2566
2689
  font,
2567
2690
  onFontChange,
2568
2691
  colors,
2569
- onColorsChange
2692
+ onColorsChange,
2693
+ toolbarStart,
2694
+ toolbarEnd
2570
2695
  }
2571
2696
  ),
2572
2697
  /* @__PURE__ */ jsx4(
@@ -2575,7 +2700,8 @@ function PagedEditorContent({
2575
2700
  className: `ink-page-card${ruled ? " ink-ruled" : ""}`,
2576
2701
  style: {
2577
2702
  width: widthPx,
2578
- minHeight: pageHeightPx,
2703
+ // In singlePage mode: fixed height + overflow hidden so content never visually spills
2704
+ ...singlePage ? { height: pageHeightPx, overflow: "hidden" } : { minHeight: pageHeightPx },
2579
2705
  padding: `${dims.paddingTopMm}mm ${dims.paddingRightMm}mm ${dims.paddingBottomMm}mm ${dims.paddingLeftMm}mm`,
2580
2706
  ["--ink-padding-top"]: `${dims.paddingTopMm}mm`,
2581
2707
  ["--ink-padding-right"]: `${dims.paddingRightMm}mm`,
@@ -2600,7 +2726,12 @@ function InkEditor({
2600
2726
  theme = "parchment",
2601
2727
  toolbar = DEFAULT_TOOLBAR,
2602
2728
  initialFont = "cursive",
2603
- initialColors
2729
+ initialColors,
2730
+ toolbarStart,
2731
+ toolbarEnd,
2732
+ singlePage = false,
2733
+ onOverflow,
2734
+ initialContent
2604
2735
  }) {
2605
2736
  const [ruled, setRuled] = useState2(false);
2606
2737
  const [font, setFont] = useState2(initialFont);
@@ -2608,14 +2739,17 @@ function InkEditor({
2608
2739
  ...theme === "minimal" ? MINIMAL_DEFAULTS : PARCHMENT_DEFAULTS,
2609
2740
  ...initialColors
2610
2741
  });
2742
+ const extensions = [
2743
+ StarterKit,
2744
+ TextAlign.configure({ types: ["heading", "paragraph"] }),
2745
+ Underline2,
2746
+ TabIndent,
2747
+ ...singlePage ? [SinglePageOverflow.configure({ pageSize, onOverflow: onOverflow ?? (() => {
2748
+ }) })] : [PageLayout.configure({ pageSize })]
2749
+ ];
2611
2750
  const editor = useEditor({
2612
- extensions: [
2613
- StarterKit,
2614
- PageLayout.configure({ pageSize }),
2615
- TextAlign.configure({ types: ["heading", "paragraph"] }),
2616
- Underline2,
2617
- TabIndent
2618
- ],
2751
+ extensions,
2752
+ content: initialContent ?? void 0,
2619
2753
  onUpdate({ editor: editor2 }) {
2620
2754
  onChange?.(editor2.getJSON());
2621
2755
  }
@@ -2637,13 +2771,17 @@ function InkEditor({
2637
2771
  font,
2638
2772
  onFontChange: setFont,
2639
2773
  colors,
2640
- onColorsChange: setColors
2774
+ onColorsChange: setColors,
2775
+ toolbarStart,
2776
+ toolbarEnd,
2777
+ singlePage
2641
2778
  }
2642
2779
  );
2643
2780
  }
2644
2781
  export {
2645
2782
  InkEditor,
2646
2783
  MINIMAL_DEFAULTS,
2647
- PARCHMENT_DEFAULTS
2784
+ PARCHMENT_DEFAULTS,
2785
+ SinglePageOverflow
2648
2786
  };
2649
2787
  //# sourceMappingURL=index.mjs.map