@helping-ai-workflow/md2doc 2.12.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/md2doc.js CHANGED
@@ -1080,18 +1080,27 @@ ${itemsHtml}
1080
1080
  // `.content { min-width: 0; flex: 1 1 auto; }` block, which
1081
1081
  // test/md2doc.test.js matches verbatim).
1082
1082
  //
1083
- // Why it exists: the gutter buttons hang OUTSIDE the content box, and the
1084
- // table row grip straddles the table's left border (half of it outside
1085
- // too — see the .ed-te-grip-row comment above). Without real padding the
1086
- // two fight over the same few pixels, which is what forced the earlier
1087
- // (now reverted) "inset the row grip into the table" hack that ended up
1088
- // covering the first cell's text. 56px of content padding plus moving the
1089
- // gutter buttons out (⠿ to left:-36px, + to left:-54px) separates them
1090
- // properly: the gutter pair occupies [contentLeft-40, contentLeft-4]
1091
- // (v2.11.1; it was [contentLeft-54, contentLeft-18] until spec §4.2's own
1092
- // numbers were restored) and the 20px-wide row grip straddles contentLeft at
1093
- // [contentLeft-10, contentLeft+10] — an 8px gap, and the grip's inner half
1094
- // stays within the cell's own 14px padding, so it never touches cell text.
1083
+ // Why it exists: the gutter buttons hang OUTSIDE the content box. 56px of
1084
+ // content padding plus moving the gutter buttons out puts the pair at
1085
+ // [contentLeft-40, contentLeft-4] (v2.11.1; it was
1086
+ // [contentLeft-54, contentLeft-18] until spec §4.2's own numbers were
1087
+ // restored), clear of .sidebar-splitter — see the .content rule below.
1088
+ //
1089
+ // GEOMETRY, AS MEASURED (S4 T1; the previous version of this comment
1090
+ // claimed "an 8px gap" between the gutter and the table row grip, a number
1091
+ // left over from the v2.11.0 pair position that v2.11.1 invalidated — the
1092
+ // real gap was MINUS 6px, i.e. an overlap):
1093
+ // ⠿ [contentLeft-22, contentLeft-4] (--ed-gutter-btn wide)
1094
+ // row grip [contentLeft, contentLeft+20] (--ed-te-grip-row-w wide)
1095
+ // The grip used to be centred on the table's left border at
1096
+ // [contentLeft-10, contentLeft+10], which overlapped the ⠿'s right 6px and
1097
+ // — being position: fixed, z-index 7, on document.body — won the hit test
1098
+ // there. spec §4.2 conflict 2's ruling (grip entirely inside the table)
1099
+ // is landed in lib/editor/client.js's updateTableEdgeGrips(); the
1100
+ // clearance that remains is exactly --ed-gutter-gap, because the ⠿'s right
1101
+ // edge and the grip's left edge are both derived from it. The grip is kept
1102
+ // off the first cell's TEXT by --ed-te-cell-pad-left below, not by the
1103
+ // 14px default padding, which is narrower than the grip.
1095
1104
  // (The padding was 48px until §4.2's hit-test conflict 1 was fixed; see the
1096
1105
  // .content rule in editModeLayoutCss below for why it is 56px now.)
1097
1106
  // S1 Task 5: the ordered-list ordinal is drawn by a CSS counter, and the
@@ -1147,10 +1156,11 @@ ${itemsHtml}
1147
1156
  further in, to spec §4.2's own [contentLeft-40, contentLeft-4] — the
1148
1157
  clearance is 16px at this padding rather than 2px. The padding stays at
1149
1158
  56px: nothing asks for the text column to move, and this measurement is
1150
- the one the §4.2 conflict-1 guard is written against.) The 20px table row grip
1151
- still straddles contentLeft at [contentLeft-10, contentLeft+10], so its
1152
- 8px gap to ⠿ is unchanged. Guarded by the elementFromPoint(splitter.right
1153
- - 2, y) assertion §4.2 asks for, in
1159
+ the one the §4.2 conflict-1 guard is written against.) The table row grip no
1160
+ longer reaches this far left at all: §4.2 conflict 2 moved it inside the
1161
+ table to [contentLeft, contentLeft+20] (S4 T1), so nothing but the gutter
1162
+ pair itself occupies [contentLeft-40, contentLeft-4]. Guarded by the
1163
+ elementFromPoint(splitter.right - 2, y) assertion §4.2 asks for, in
1154
1164
  test/editor-client-runtime.test.js. */
1155
1165
  .content { padding-left: 56px; }
1156
1166
 
@@ -1221,9 +1231,10 @@ ${itemsHtml}
1221
1231
  [contentLeft-54, contentLeft-18], which with 56px of padding sits wholly
1222
1232
  inside the content column (2px clear of .sidebar-splitter's right edge —
1223
1233
  see the padding rule's own comment for the measurement), so + never
1224
- reaches a negative viewport x and never covers the splitter. The 20px
1225
- table row grip still straddles contentLeft at
1226
- [contentLeft-10, contentLeft+10] - an 8px gap, unchanged. */
1234
+ reaches a negative viewport x and never covers the splitter. The table
1235
+ row grip is at [contentLeft, contentLeft+20] since §4.2 conflict 2
1236
+ landed (S4 T1), so the clearance from the ⠿'s right edge is exactly
1237
+ --ed-gutter-gap and cannot drift: both edges are derived from it. */
1227
1238
  /* Stated ONCE, as tokens, because §4.2's three numbers are not independent:
1228
1239
  the gutter is exactly two buttons wide plus the deliberate right-hand
1229
1240
  breathing gap, and the hover zone below is exactly the gutter. Writing any
@@ -1232,6 +1243,19 @@ ${itemsHtml}
1232
1243
  --ed-gutter-btn: 18px;
1233
1244
  --ed-gutter-gap: 4px;
1234
1245
  --ed-gutter-w: calc(var(--ed-gutter-btn) * 2 + var(--ed-gutter-gap));
1246
+ /* spec §4.2 hit-test conflict 2, landed S4 T1. The table row grip now
1247
+ sits ENTIRELY inside the table (its LEFT edge on the table's left
1248
+ border — see updateTableEdgeGrips() in lib/editor/client.js), which
1249
+ leaves the ⠿'s right edge and the grip's left edge exactly
1250
+ --ed-gutter-gap apart: the 4px breathing gap §4.2 already keeps on the
1251
+ right of the gutter pair IS the separation. Nothing new is written
1252
+ down, so nothing new can drift.
1253
+ The consequence is this token: the grip is 20px wide and the first
1254
+ column's default padding is 14px, so an inset grip would sit on that
1255
+ cell's TEXT — the v2.10.1 user-acceptance defect that got the first
1256
+ attempt at this ruling reverted. The first column is padded past the
1257
+ grip instead, by the grip's own width plus that same gap. */
1258
+ --ed-te-cell-pad-left: calc(var(--ed-te-grip-row-w) + var(--ed-gutter-gap));
1235
1259
  }
1236
1260
  .ed-handle {
1237
1261
  left: calc(-1 * (var(--ed-gutter-btn) + var(--ed-gutter-gap))); top: 0;
@@ -1283,6 +1307,16 @@ ${itemsHtml}
1283
1307
  content: ""; position: absolute;
1284
1308
  left: calc(-1 * var(--ed-gutter-w)); top: 0;
1285
1309
  width: var(--ed-gutter-w); height: 100%;
1310
+ }
1311
+
1312
+ /* §4.2 conflict 2's other half: the row grip lives inside the first column
1313
+ now, so that column's cells must be padded past it. EDIT MODE ONLY —
1314
+ reader HTML and PDF output keep the shared 7px 14px and stay
1315
+ byte-identical. Only the LEFT padding changes, and only on the first
1316
+ cell of each row; every other cell is untouched. */
1317
+ .ed-block[data-block-type="table"] th:first-child,
1318
+ .ed-block[data-block-type="table"] td:first-child {
1319
+ padding-left: var(--ed-te-cell-pad-left);
1286
1320
  }`;
1287
1321
 
1288
1322
  const html = `<!DOCTYPE html>
@@ -2296,17 +2330,49 @@ ${itemsHtml}
2296
2330
  }
2297
2331
  .ed-te-drop-indicator[hidden] { display: none; }
2298
2332
  .ed-te-row-dragging { opacity: 0.4; }
2333
+ /* S4 Task 2 (spec §4.5): the block drag's own singleton drop indicator,
2334
+ shown while a block is being dragged by its ⠿. Same 3px blue rule as the
2335
+ table's, on a SEPARATE node rather than sharing '.ed-te-drop-indicator':
2336
+ that one is shared by the row and column axes and each of them has to
2337
+ rewrite the OTHER axis's dimension on every move or a stale value leaks
2338
+ in — a third writer multiplies that coupling. It is still ONE node for the
2339
+ whole document (the Global Constraint is "never one per boundary"), and
2340
+ the separate class is what lets a test assert that a block drag raised
2341
+ the BLOCK line rather than table chrome that happened to be on screen. */
2342
+ .ed-block-drop-indicator {
2343
+ position: fixed; z-index: 8; height: 3px; border-radius: 2px;
2344
+ background: #3b82f6; pointer-events: none;
2345
+ }
2346
+ .ed-block-drop-indicator[hidden] { display: none; }
2347
+ /* S4 Task 2b (user request, 2026-09-01): the drag's feedback on the SOURCE
2348
+ side. lib/editor/client.js puts '.ed-block-dragging' on <html> the moment
2349
+ a ⠿ drag engages and takes it off in teardownBlockDrag(), which every one
2350
+ of the five abort paths funnels through. On <html> rather than on the
2351
+ dragged block because the plan forbids mutating anything inside
2352
+ '.content' during a drag — that invariant is the only reason Escape /
2353
+ pointercancel / window blur can all clean up with nothing to revert.
2354
+ The universal selector plus !important is what the rule needs to be worth
2355
+ having: '.ed-handle' (the button the gesture starts on), '.ed-handle-menu-btn'
2356
+ and every table grip carry their own 'cursor: pointer', and a plain
2357
+ inherited cursor loses to all of them. */
2358
+ html.ed-block-dragging, html.ed-block-dragging * { cursor: grabbing !important; }
2299
2359
  /* Notion-style grip handles: replace the original invisible TE_EDGE_PX=8
2300
2360
  proximity zone (user-acceptance feedback: unusably small, no visible
2301
2361
  affordance) with two real, adequately-sized (>=18x24px) click/drag
2302
2362
  targets. '.ed-te-grip-row' is a vertical 6-dot handle shown at the LEFT
2303
2363
  EDGE of the hovered row -- every row, the HEADER included (spec 3.10:
2304
- the header is draggable too), STRADDLING the table's left border (its
2305
- centreline ON that border), which is the same rule '.ed-te-grip-col'
2306
- uses on the table's TOP border -- one geometry for both axes, no
2307
- per-row-type special case. The block's own gutter is kept clear of it by
2308
- the edit-mode-only layout chunk emitted near the end of this stylesheet,
2309
- not by insetting the grip; '.ed-te-grip-col' is a horizontal 6-dot
2364
+ the header is draggable too), sitting ENTIRELY INSIDE the table with its
2365
+ LEFT edge on the table's left border (spec 4.2 hit-test conflict 2,
2366
+ landed in S4 T1). The two axes are therefore NOT mirror images any more:
2367
+ '.ed-te-grip-col' still straddles the table's TOP border, because nothing
2368
+ of the page's own chrome lives up there. The row axis cannot: MEASURED on
2369
+ v2.12.0 at 1400x900, the straddling grip occupied [tableLeft-10,
2370
+ tableLeft+10] while the block's own '.ed-handle' occupied [blockLeft-22,
2371
+ blockLeft-4] with tableLeft == blockLeft, so the grip (position: fixed,
2372
+ z-index 7, on document.body) won the hit test for the RIGHT 6px of every
2373
+ ⠿ whose Y band it reached. The first column's own padding is widened in
2374
+ the edit-mode-only layout chunk near the end of this stylesheet so the
2375
+ inset grip still never reaches cell TEXT; '.ed-te-grip-col' is a horizontal 6-dot
2310
2376
  handle shown just ABOVE the hovered column (every column). Dots are plain <span>s laid out via CSS grid with
2311
2377
  place-content: center, so the dot cluster stays compact/centered
2312
2378
  regardless of the button's own (larger, hit-target-sized) box — no
@@ -2325,8 +2391,15 @@ ${itemsHtml}
2325
2391
  .ed-te-grip:hover { background: rgba(0, 0, 0, 0.06); }
2326
2392
  .ed-te-grip-dot { width: 3px; height: 3px; border-radius: 50%; background: #9ca3af; }
2327
2393
  .ed-te-grip:hover .ed-te-grip-dot { background: #6b7280; }
2394
+ /* spec §4.2 hit-test conflict 2 (S4 T1): the row grip's width is a TOKEN
2395
+ because two other numbers are derived from it — lib/editor/client.js pins
2396
+ the grip's LEFT edge to the table's left border (so the whole grip is
2397
+ inside the first column and shares no pixel with the block gutter's ⠿),
2398
+ and the edit-mode first-column padding must be wide enough to host it
2399
+ without covering cell text. Written once here, derived twice below. */
2400
+ :root { --ed-te-grip-row-w: 20px; }
2328
2401
  .ed-te-grip-row {
2329
- width: 20px; height: 28px; cursor: grab; z-index: 7;
2402
+ width: var(--ed-te-grip-row-w); height: 28px; cursor: grab; z-index: 7;
2330
2403
  grid-template-columns: repeat(2, 3px); grid-template-rows: repeat(3, 3px);
2331
2404
  gap: 3px 4px;
2332
2405
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helping-ai-workflow/md2doc",
3
- "version": "2.12.0",
3
+ "version": "3.0.0",
4
4
  "description": "Markdown → HTML / PDF renderer with WaveDrom, Mermaid, and Graphviz support",
5
5
  "keywords": [
6
6
  "markdown",