@ixfx/components 0.2.6 → 0.3.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.
Files changed (140) hide show
  1. package/bundle/index.d.ts +213 -117
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +1356 -658
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +3 -3
  6. package/dist/ac-text.js.map +1 -1
  7. package/dist/{button-B9ztaKyl.js → button-DMt5XWUK.js} +4 -4
  8. package/dist/{button-B9ztaKyl.js.map → button-DMt5XWUK.js.map} +1 -1
  9. package/dist/button.js +1 -1
  10. package/dist/checkbox.d.ts +2 -3
  11. package/dist/checkbox.d.ts.map +1 -1
  12. package/dist/checkbox.js +1 -1
  13. package/dist/checkbox.js.map +1 -1
  14. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js → colour-C3MQIjFJ-BLXg8W3U.js} +2 -2
  15. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js.map → colour-C3MQIjFJ-BLXg8W3U.js.map} +1 -1
  16. package/dist/{colour-picker-BNqDz4wK.js → colour-picker-K6zu82lm.js} +5 -5
  17. package/dist/{colour-picker-BNqDz4wK.js.map → colour-picker-K6zu82lm.js.map} +1 -1
  18. package/dist/colour-picker.js +1 -1
  19. package/dist/crumbs.d.ts +2 -3
  20. package/dist/crumbs.d.ts.map +1 -1
  21. package/dist/crumbs.js +10 -10
  22. package/dist/crumbs.js.map +1 -1
  23. package/dist/data-display.js +3 -3
  24. package/dist/{decorate-O4m1U4l3.js → decorate-D7rC1gLP.js} +10 -8
  25. package/dist/decorate-D7rC1gLP.js.map +1 -0
  26. package/dist/{defaults-FEZaGiYZ.js → defaults-C7elhuyJ.js} +2 -2
  27. package/dist/{defaults-FEZaGiYZ.js.map → defaults-C7elhuyJ.js.map} +1 -1
  28. package/dist/{dist-By5URG0Y.js → dist-BX0OVIm7.js} +2 -2
  29. package/dist/{dist-By5URG0Y.js.map → dist-BX0OVIm7.js.map} +1 -1
  30. package/dist/{dist-D7kJgnSE.js → dist-TAGYwaju.js} +1 -1
  31. package/dist/{dist-D7kJgnSE.js.map → dist-TAGYwaju.js.map} +1 -1
  32. package/dist/editable-label.d.ts +6 -1
  33. package/dist/editable-label.d.ts.map +1 -1
  34. package/dist/editable-label.js +49 -2
  35. package/dist/editable-label.js.map +1 -1
  36. package/dist/{hex-editor-gH3mteMr.js → hex-editor-CsN_ySkH.js} +85 -85
  37. package/dist/hex-editor-CsN_ySkH.js.map +1 -0
  38. package/dist/{hex-editor-2_s0Ua5t.d.ts → hex-editor-DlfoA65y.d.ts} +5 -5
  39. package/dist/{hex-editor-2_s0Ua5t.d.ts.map → hex-editor-DlfoA65y.d.ts.map} +1 -1
  40. package/dist/hex.d.ts +1 -1
  41. package/dist/hex.js +1 -1
  42. package/dist/{highlight-CAjwQ4GE.js → highlight-TPbSHruI.js} +3 -3
  43. package/dist/highlight-TPbSHruI.js.map +1 -0
  44. package/dist/{icon-CYACf6o8.js → icon-9NWTXtE1.js} +4 -5
  45. package/dist/icon-9NWTXtE1.js.map +1 -0
  46. package/dist/icon-CeTDJhC7.d.ts.map +1 -1
  47. package/dist/icons.js +3 -3
  48. package/dist/{incr-search-BZawvrfl.js → incr-search-DSKKsk6w.js} +12 -10
  49. package/dist/incr-search-DSKKsk6w.js.map +1 -0
  50. package/dist/incr-search.d.ts +1 -1
  51. package/dist/incr-search.js +2 -2
  52. package/dist/{index-CwGkb9s1.d.ts → index--hrxPBkQ.d.ts} +69 -69
  53. package/dist/index--hrxPBkQ.d.ts.map +1 -0
  54. package/dist/{index-kY5gxbkI.d.ts → index-DSlPeWLK.d.ts} +15 -15
  55. package/dist/{index-kY5gxbkI.d.ts.map → index-DSlPeWLK.d.ts.map} +1 -1
  56. package/dist/{index-V2cJorlA.d.ts → index-hXFn14tM.d.ts} +4 -4
  57. package/dist/index-hXFn14tM.d.ts.map +1 -0
  58. package/dist/index.d.ts +109 -13
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +666 -24
  61. package/dist/index.js.map +1 -1
  62. package/dist/keyboard-De_vOHLg.js.map +1 -1
  63. package/dist/{labelled-input-base-BvgH7ldm.js → labelled-input-base-CqPL5rKm.js} +2 -2
  64. package/dist/labelled-input-base-CqPL5rKm.js.map +1 -0
  65. package/dist/labelled-radial-input.js +3 -3
  66. package/dist/labelled-radial-input.js.map +1 -1
  67. package/dist/labelled-range-input.js +2 -2
  68. package/dist/led.js +1 -1
  69. package/dist/{menu-C3eANB5M.js → menu-DJJuCm-5.js} +48 -48
  70. package/dist/menu-DJJuCm-5.js.map +1 -0
  71. package/dist/{menu-item-6IU5weHi.js → menu-item-CKRfP0pN.js} +10 -10
  72. package/dist/menu-item-CKRfP0pN.js.map +1 -0
  73. package/dist/menu.d.ts +1 -1
  74. package/dist/menu.js +2 -2
  75. package/dist/miller.d.ts +5 -7
  76. package/dist/miller.d.ts.map +1 -1
  77. package/dist/miller.js +14 -14
  78. package/dist/miller.js.map +1 -1
  79. package/dist/narrowed-text.d.ts.map +1 -1
  80. package/dist/narrowed-text.js +4 -4
  81. package/dist/narrowed-text.js.map +1 -1
  82. package/dist/panel.js +2 -2
  83. package/dist/panel.js.map +1 -1
  84. package/dist/plots.js +1 -1
  85. package/dist/polar-pad.d.ts.map +1 -1
  86. package/dist/polar-pad.js +1 -1
  87. package/dist/polar-pad.js.map +1 -1
  88. package/dist/{popup-Ddx9LCzY.js → popup-ImtS9KnS.js} +1 -1
  89. package/dist/{popup-Ddx9LCzY.js.map → popup-ImtS9KnS.js.map} +1 -1
  90. package/dist/{prominence-CreDBdCs.js → prominence--ic9uWBl.js} +1 -1
  91. package/dist/{prominence-CreDBdCs.js.map → prominence--ic9uWBl.js.map} +1 -1
  92. package/dist/{radial-input-Dbc6fgCK.js → radial-input-DZA6WaFu.js} +14 -14
  93. package/dist/radial-input-DZA6WaFu.js.map +1 -0
  94. package/dist/radial-input.js +1 -1
  95. package/dist/range-input.js +1 -1
  96. package/dist/range.js +2 -2
  97. package/dist/{registry-CZEB1YI9.js → registry-Q2gYQCHM.js} +1 -1
  98. package/dist/{registry-CZEB1YI9.js.map → registry-Q2gYQCHM.js.map} +1 -1
  99. package/dist/selecthorizontal.js +1 -1
  100. package/dist/snap-container.js +1 -1
  101. package/dist/split-layout.js +4 -4
  102. package/dist/split-layout.js.map +1 -1
  103. package/dist/swipe.d.ts +1 -2
  104. package/dist/swipe.d.ts.map +1 -1
  105. package/dist/swipe.js +2 -2
  106. package/dist/swipe.js.map +1 -1
  107. package/dist/tabs.js +2 -2
  108. package/dist/tabs.js.map +1 -1
  109. package/dist/{timeline-B62Sz6J_.js → timeline-Cv75Th2U.js} +120 -116
  110. package/dist/timeline-Cv75Th2U.js.map +1 -0
  111. package/dist/timeline.d.ts +1 -1
  112. package/dist/timeline.js +1 -1
  113. package/dist/tooltip-CwGfb4lp.d.ts.map +1 -1
  114. package/dist/tree-CvvKbO-f.js +1330 -0
  115. package/dist/tree-CvvKbO-f.js.map +1 -0
  116. package/dist/{tree-component-CQpoPo4s.d.ts → tree-component-D8Bg48tt.d.ts} +12 -12
  117. package/dist/{tree-component-CQpoPo4s.d.ts.map → tree-component-D8Bg48tt.d.ts.map} +1 -1
  118. package/dist/tree.d.ts +1 -1
  119. package/dist/tree.js +1 -1143
  120. package/dist/xy-axis-D9uwpGTT.d.ts.map +1 -1
  121. package/dist/{xy-axis-C7fNxDwY.js → xy-axis-DL0tfJDr.js} +10 -10
  122. package/dist/{xy-axis-C7fNxDwY.js.map → xy-axis-DL0tfJDr.js.map} +1 -1
  123. package/dist/xy-pad.js +2 -2
  124. package/package.json +3 -3
  125. package/dist/decorate-O4m1U4l3.js.map +0 -1
  126. package/dist/hex-editor-gH3mteMr.js.map +0 -1
  127. package/dist/highlight-CAjwQ4GE.js.map +0 -1
  128. package/dist/icon-CYACf6o8.js.map +0 -1
  129. package/dist/incr-search-BZawvrfl.js.map +0 -1
  130. package/dist/index-CwGkb9s1.d.ts.map +0 -1
  131. package/dist/index-V2cJorlA.d.ts.map +0 -1
  132. package/dist/labelled-input-base-BvgH7ldm.js.map +0 -1
  133. package/dist/menu-C3eANB5M.js.map +0 -1
  134. package/dist/menu-item-6IU5weHi.js.map +0 -1
  135. package/dist/radial-input-Dbc6fgCK.js.map +0 -1
  136. package/dist/timeline-B62Sz6J_.js.map +0 -1
  137. package/dist/tree-data-model-DGvRVOFY.js +0 -190
  138. package/dist/tree-data-model-DGvRVOFY.js.map +0 -1
  139. package/dist/tree.js.map +0 -1
  140. /package/dist/{interaction-CQDQJf2Q.js → interaction-DG7X1rhI.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"radial-input-Dbc6fgCK.js","names":["#_radius","#dragging","#dragStartY","#valueAtDragStart","#onPointerMove","#onPointerUp","#setValueFromRelative","#getRelativeValue","#lanes","#quantise","#isUserInteraction","#scheduleChangeEvent","#fireChangeEvent","#changeDebounceTimer","#getCssVar","#getEffectiveMarkerStyle","#getNeedlePath","#onPointerDown","#onKeyDown","#renderCircleFill","#renderWedge","#renderCircleBorder","#renderLane","#renderMidpointMarker","#renderValueMarker","customElement"],"sources":["../src/radial-input/svg-utils.ts","../src/radial-input/radial-input.ts"],"sourcesContent":["/**\n * SVG utilities for radial input component\n * Handles geometry calculations for flattened circle arcs\n */\n\nimport type { ValueMode } from './types.js';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface ArcParameters {\n startAngle: number; // radians\n endAngle: number; // radians\n radius: number;\n center: Point;\n}\n\nconst PI2 = Math.PI * 2;\nconst MIDPOINT_ANGLE = -Math.PI / 2; // Top-center of circle\n\n/**\n * Convert polar coordinates (angle in radians, radius) to SVG Cartesian coordinates\n * @param angle - Angle in radians (0 = right/east, increases clockwise in SVG)\n * @param radius - Distance from center\n * @param center - Center point (default: {x: 50, y: 50})\n * @returns Point in SVG coordinates\n */\nexport function polarToCartesian(\n angle: number,\n radius: number,\n center: Point = { x: 50, y: 50 }\n): Point {\n return {\n x: center.x + Math.cos(angle) * radius,\n y: center.y + Math.sin(angle) * radius,\n };\n}\n\n/**\n * Convert SVG Cartesian coordinates to polar (angle in radians, radius)\n * @param x - X coordinate\n * @param y - Y coordinate\n * @param center - Center point (default: {x: 50, y: 50})\n * @returns Angle in radians (0..2π) and radius\n */\nexport function cartesianToPolar(\n x: number,\n y: number,\n center: Point = { x: 50, y: 50 }\n): { angle: number; radius: number } {\n const dx = x - center.x;\n const dy = y - center.y;\n const radius = Math.sqrt(dx * dx + dy * dy);\n const raw = Math.atan2(dy, dx);\n const angle = ((raw % PI2) + PI2) % PI2; // Normalize to 0..2π\n return { angle, radius };\n}\n\n/**\n * Calculate start and end angles for a flattened circle\n * @param flatten - How much of the circle to hide (0..1): 0 = full circle, 0.5 = half circle, 1 = no arc\n * @returns Start and end angles in radians\n */\nexport function getFlattenedCircleAngles(flatten: number): {\n startAngle: number;\n endAngle: number;\n totalArc: number;\n} {\n // flatten represents the percentage of circle hidden\n // 0 = full circle (360°), 0.5 = half circle (180°), 1 = no arc (0°)\n const visibleArc = PI2 * (1 - flatten); // Total visible arc in radians\n \n // Center the visible arc at the top (angle = -π/2)\n // Start and end angles are symmetric around top-center\n const halfArc = visibleArc / 2;\n const topAngle = -Math.PI / 2; // Top center (270° in standard coords, but -90° from east)\n \n const startAngle = topAngle - halfArc;\n const endAngle = topAngle + halfArc;\n const totalArc = endAngle - startAngle;\n \n return { startAngle, endAngle, totalArc };\n}\n\n/**\n * Map a value (0..1) to an angle based on value mode\n * @param relativeValue - Value in 0..1 range\n * @param valueMode - How to interpret the value\n * @param flatten - Circle flattening amount (0..1)\n * @returns Angle in radians\n */\nexport function valueToAngle(\n relativeValue: number,\n valueMode: ValueMode,\n flatten: number\n): number {\n const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);\n \n switch (valueMode) {\n case 'range':\n // Linear mapping from start to end\n return startAngle + relativeValue * totalArc;\n \n case 'bipolar': {\n // Midpoint is at top (MIDPOINT_ANGLE = -π/2)\n if (relativeValue <= 0.5) {\n // Map 0..0.5 to startAngle..MIDPOINT_ANGLE (going counterclockwise to top)\n const t = relativeValue / 0.5;\n return startAngle + t * (MIDPOINT_ANGLE - startAngle);\n } else {\n // Map 0.5..1 to MIDPOINT_ANGLE..endAngle (going clockwise from top)\n const t = (relativeValue - 0.5) / 0.5;\n return MIDPOINT_ANGLE + t * (endAngle - MIDPOINT_ANGLE);\n }\n }\n \n case 'unipolar': {\n // Marker stays at top for unipolar mode\n return MIDPOINT_ANGLE;\n }\n \n default:\n return startAngle;\n }\n}\n\n/**\n * Get start and end angles for a value arc\n * @param relativeValue - Value in 0..1 range\n * @param valueMode - How to interpret the value\n * @param flatten - Circle flattening amount (0..1)\n * @returns Start and end angles for the arc\n */\nexport function getValueArcAngles(\n relativeValue: number,\n valueMode: ValueMode,\n flatten: number\n): { startAngle: number; endAngle: number } {\n const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);\n \n switch (valueMode) {\n case 'range':\n // Arc from start to value position\n return {\n startAngle,\n endAngle: startAngle + relativeValue * totalArc,\n };\n \n case 'bipolar': {\n // Midpoint is at top (MIDPOINT_ANGLE = -π/2)\n if (relativeValue < 0.5) {\n // Arc from some point toward top (going counterclockwise)\n const t = relativeValue / 0.5;\n const valueAngle = startAngle + t * (MIDPOINT_ANGLE - startAngle);\n return {\n startAngle: valueAngle,\n endAngle: MIDPOINT_ANGLE,\n };\n } else if (relativeValue > 0.5) {\n // Arc from top going clockwise\n const t = (relativeValue - 0.5) / 0.5;\n const valueAngle = MIDPOINT_ANGLE + t * (endAngle - MIDPOINT_ANGLE);\n return {\n startAngle: MIDPOINT_ANGLE,\n endAngle: valueAngle,\n };\n } else {\n // Exactly at midpoint, no arc\n return { startAngle: MIDPOINT_ANGLE, endAngle: MIDPOINT_ANGLE };\n }\n }\n \n case 'unipolar': {\n // Spread symmetrically from top (MIDPOINT_ANGLE = -π/2)\n const spreadAmount = relativeValue * totalArc / 2;\n return {\n startAngle: MIDPOINT_ANGLE - spreadAmount,\n endAngle: MIDPOINT_ANGLE + spreadAmount,\n };\n }\n \n default:\n return { startAngle, endAngle: startAngle };\n }\n}\n\n/**\n * Get start and end angles for an anchored arc\n * Centered on current value position, extends equally in both directions\n * @param relativeValue - Current value position (0..1)\n * @param laneValue - Width of the arc (0..1, where 1 = full range)\n * @param valueMode - How to interpret the value\n * @param flatten - Circle flattening amount (0..1)\n * @returns Start and end angles for the arc\n */\nexport function getAnchoredArcAngles(\n relativeValue: number,\n laneValue: number,\n valueMode: ValueMode,\n flatten: number\n): { startAngle: number; endAngle: number } {\n const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);\n \n // Get the angle for the current value (this is the center of the arc)\n const centerAngle = valueToAngle(relativeValue, valueMode, flatten);\n \n // Calculate half-width of the arc\n // laneValue is 0..1, where 1 = full range\n const halfWidth = (laneValue * totalArc) / 2;\n \n let arcStart = centerAngle - halfWidth;\n let arcEnd = centerAngle + halfWidth;\n \n // Clamp to valid bounds (prevent wrapping)\n // The valid range is [startAngle, endAngle]\n if (arcStart < startAngle) arcStart = startAngle;\n if (arcEnd > endAngle) arcEnd = endAngle;\n \n // If the arc would be degenerate after clamping, return a single point\n if (arcStart >= arcEnd) {\n return { startAngle: centerAngle, endAngle: centerAngle };\n }\n \n return { startAngle: arcStart, endAngle: arcEnd };\n}\n\n/**\n * Generate SVG path for an arc\n * @param startAngle - Start angle in radians\n * @param endAngle - End angle in radians\n * @param radius - Arc radius\n * @param center - Center point\n * @returns SVG path data string\n */\nexport function generateArcPath(\n startAngle: number,\n endAngle: number,\n radius: number,\n center: Point = { x: 50, y: 50 }\n): string {\n if (startAngle === endAngle) {\n return ''; // No arc to draw\n }\n\n // Calculate arc length to determine large-arc-flag\n let arcLength = endAngle - startAngle;\n \n // Handle wrap-around case (e.g., from 5.5 to 0.5 radians)\n if (arcLength < 0) {\n arcLength += PI2;\n }\n \n // Handle full or near-full circle case\n // When arcLength is nearly 2π, start and end points are essentially the same\n // and drawing a full circle needs special handling\n if (arcLength >= PI2 - 0.001) {\n // Draw a full circle by going almost all the way around\n const adjustedEnd = startAngle + PI2 - 0.001;\n const start = polarToCartesian(startAngle, radius, center);\n const end = polarToCartesian(adjustedEnd, radius, center);\n return `M ${start.x},${start.y} A ${radius},${radius} 0 1 1 ${end.x},${end.y}`;\n }\n\n const start = polarToCartesian(startAngle, radius, center);\n const end = polarToCartesian(endAngle, radius, center);\n \n const largeArcFlag = arcLength > Math.PI ? 1 : 0;\n \n return `M ${start.x},${start.y} A ${radius},${radius} 0 ${largeArcFlag} 1 ${end.x},${end.y}`;\n}\n\n/**\n * Generate SVG path for a full flattened circle border\n * @param radius - Circle radius\n * @param flatten - Flattening amount (0..1)\n * @param center - Center point\n * @returns SVG path data string\n */\nexport function generateCircleBorderPath(\n radius: number,\n flatten: number,\n center: Point = { x: 50, y: 50 }\n): string {\n const { startAngle, endAngle } = getFlattenedCircleAngles(flatten);\n return generateArcPath(startAngle, endAngle, radius, center);\n}\n\n/**\n * Generate SVG path data for a filled wedge (pie-slice) sector\n * @param startAngle - Start angle in radians\n * @param endAngle - End angle in radians\n * @param radius - Wedge radius (same as dial radius)\n * @param center - Center point\n * @param clockwise - Whether to draw arc clockwise (true) or counterclockwise (false)\n * @returns SVG path data string for filled wedge\n */\nexport function generateWedgePath(\n startAngle: number,\n endAngle: number,\n radius: number,\n center: Point = { x: 50, y: 50 },\n clockwise: boolean = true\n): string {\n if (startAngle === endAngle) {\n return ''; // No wedge to draw\n }\n\n // Calculate arc length (always positive, representing shortest path)\n let arcLength = endAngle - startAngle;\n if (arcLength < 0) {\n arcLength += PI2;\n }\n \n // Handle full or near-full circle case\n if (arcLength >= PI2 - 0.001) {\n // Draw a near-full circle wedge\n const start = polarToCartesian(startAngle, radius, center);\n const adjustedEnd = startAngle + PI2 - 0.001;\n const adjEnd = polarToCartesian(adjustedEnd, radius, center);\n const largeArcFlag = 1;\n const sweepFlag = clockwise ? 1 : 0;\n return `M ${center.x},${center.y} L ${start.x},${start.y} A ${radius},${radius} 0 ${largeArcFlag} ${sweepFlag} ${adjEnd.x},${adjEnd.y} Z`;\n }\n \n let actualStartAngle = startAngle;\n let actualEndAngle = endAngle;\n let actualClockwise = clockwise;\n \n // When going counterclockwise and arcLength > π, we want the short clockwise path instead\n // by swapping start and end\n if (!clockwise && arcLength > Math.PI) {\n actualStartAngle = endAngle;\n actualEndAngle = startAngle;\n actualClockwise = true;\n arcLength = PI2 - arcLength;\n }\n \n const start = polarToCartesian(actualStartAngle, radius, center);\n const end = polarToCartesian(actualEndAngle, radius, center);\n \n const largeArcFlag = arcLength > Math.PI ? 1 : 0;\n const sweepFlag = actualClockwise ? 1 : 0;\n \n // M center L start A radius radius 0 large sweep end Z\n return `M ${center.x},${center.y} L ${start.x},${start.y} A ${radius},${radius} 0 ${largeArcFlag} ${sweepFlag} ${end.x},${end.y} Z`;\n}\n\n/**\n * Get wedge angles based on value mode\n * @param relativeValue - Value in 0..1 range\n * @param valueMode - How to interpret the value\n * @param flatten - Circle flattening amount (0..1)\n * @returns Object with startAngle, endAngle, and clockwise flag\n */\nexport function getWedgeAngles(\n relativeValue: number,\n valueMode: ValueMode,\n flatten: number\n): { startAngle: number; endAngle: number; clockwise: boolean } {\n const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);\n const valueAngle = valueToAngle(relativeValue, valueMode, flatten);\n \n switch (valueMode) {\n case 'range':\n // Wedge from startAngle to valueAngle (always clockwise)\n return {\n startAngle,\n endAngle: valueAngle,\n clockwise: true,\n };\n \n case 'bipolar': {\n // When value < 0.5, go counterclockwise from midpoint to value\n // When value >= 0.5, go clockwise from midpoint to value\n if (relativeValue < 0.5) {\n return {\n startAngle: MIDPOINT_ANGLE,\n endAngle: valueAngle,\n clockwise: false,\n };\n } else {\n return {\n startAngle: MIDPOINT_ANGLE,\n endAngle: valueAngle,\n clockwise: true,\n };\n }\n }\n \n case 'unipolar': {\n // Wedge centered at MIDPOINT, spreading value * totalArc/2 in each direction\n const halfSpread = relativeValue * totalArc / 2;\n return {\n startAngle: MIDPOINT_ANGLE - halfSpread,\n endAngle: MIDPOINT_ANGLE + halfSpread,\n clockwise: true,\n };\n }\n \n default:\n return { startAngle, endAngle: startAngle, clockwise: true };\n }\n}\n\n/**\n * Get position for a marker (needle or dot) at a given value\n * @param relativeValue - Value in 0..1 range\n * @param valueMode - How to interpret the value\n * @param flatten - Circle flattening amount (0..1)\n * @param radius - Circle radius\n * @param center - Center point\n * @returns Angle in radians and position point\n */\nexport function getMarkerPosition(\n relativeValue: number,\n valueMode: ValueMode,\n flatten: number,\n radius: number,\n center: Point = { x: 50, y: 50 }\n): { angle: number; position: Point } {\n const angle = valueToAngle(relativeValue, valueMode, flatten);\n const position = polarToCartesian(angle, radius, center);\n return { angle, position };\n}\n\n/**\n * Get position for midpoint marker (used in bipolar/unipolar modes)\n * @param radius - Circle radius\n * @param center - Center point\n * @returns Position at top-center of circle\n */\nexport function getMidpointPosition(\n radius: number,\n center: Point = { x: 50, y: 50 }\n): { angle: number; position: Point } {\n const position = polarToCartesian(MIDPOINT_ANGLE, radius, center);\n return { angle: MIDPOINT_ANGLE, position };\n}\n\n/**\n * Clamp a value between min and max\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Scale a value from one range to another\n */\nexport function scale(\n value: number,\n fromMin: number,\n fromMax: number,\n toMin: number,\n toMax: number\n): number {\n const normalized = (value - fromMin) / (fromMax - fromMin);\n return toMin + normalized * (toMax - toMin);\n}\n","/**\n * Radial input component\n * A rotary dial input inspired by Ableton Live and music production software\n */\n\nimport type { PropertyValues } from 'lit';\nimport type {\n LaneData,\n LaneType,\n MarkerStyle,\n RadialInputChangeDetail,\n RadialInputInputDetail,\n ValueMode,\n} from './types.js';\nimport { css, html, LitElement, svg } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { KEY_STEP_DIVISOR, resolveStep } from '../keyboard.js';\nimport { prominenceStyles } from '../styles/prominence.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport {\n clamp,\n generateArcPath,\n generateCircleBorderPath,\n generateWedgePath,\n getAnchoredArcAngles,\n getMarkerPosition,\n getMidpointPosition,\n getValueArcAngles,\n getWedgeAngles,\n scale,\n} from './svg-utils.js';\n\nconst CENTER = { x: 50, y: 50 }; // Center point in viewBox\n\n@customElement(`ixfx-radial-input`)\nexport class RadialInputElement extends LitElement {\n // Internal backing field for radius (used with custom setter for clamping)\n #_radius = 45;\n\n // Radius property (controls dial size, 10-50 range to prevent clipping)\n @property({ type: Number })\n get radius(): number {\n return this.#_radius;\n }\n\n set radius(v: number) {\n this.#_radius = clamp(v, 10, 50);\n }\n\n // Value properties (matches ixfx-range API)\n @property({ type: Number })\n value = 0;\n\n @property({ type: Number })\n min = 0;\n\n @property({ type: Number })\n max = 100;\n\n @property({ type: Number })\n step: number | undefined = undefined;\n\n @property({ type: Number })\n stops?: number;\n\n // Value mode\n @property({ type: String, attribute: `value-mode` })\n valueMode: ValueMode = `range`;\n\n // Appearance\n @property({ type: Number })\n flatten = 0.3;\n\n @property({ type: String, attribute: `marker-style` })\n markerStyle: MarkerStyle = `needle`;\n\n @property({ type: Boolean })\n wedge = false;\n\n // Interaction\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // Internal state\n #lanes: Map<string, LaneData> = new Map();\n #dragging = false;\n #dragStartY = 0;\n #valueAtDragStart = 0;\n #changeDebounceTimer: number | undefined;\n #isUserInteraction = false;\n\n constructor() {\n super();\n // Initialize default lanes\n // Note: value lane will be updated in willUpdate() to reflect initial value\n this.#lanes.set(`track`, {\n key: `track`,\n value: 1, // Full range\n type: `fixed`,\n offset: 0,\n width: 0.16,\n });\n this.#lanes.set(`value`, {\n key: `value`,\n value: 0, // Will be updated in willUpdate()\n type: `value`,\n offset: 0,\n width: 0.24,\n });\n }\n\n // Lane management API\n addLane(\n key: string,\n options?: {\n value?: number;\n type?: LaneType;\n offset?: number;\n width?: number;\n },\n ): void {\n const relativeValue = this.#getRelativeValue();\n this.#lanes.set(key, {\n key,\n value: options?.value ?? relativeValue,\n type: options?.type ?? `value`,\n offset: options?.offset ?? 0,\n width: options?.width ?? 0.1,\n });\n this.requestUpdate();\n }\n\n setLaneValue(key: string, value: number): void {\n const lane = this.#lanes.get(key);\n if (lane) {\n lane.value = clamp(value, 0, 1);\n this.requestUpdate();\n }\n }\n\n removeLane(key: string): void {\n this.#lanes.delete(key);\n this.requestUpdate();\n }\n\n getLanes(): ReadonlyMap<string, LaneData> {\n return new Map(this.#lanes);\n }\n\n getLane(key: string): LaneData | undefined {\n return this.#lanes.get(key);\n }\n\n // Internal helpers\n #getRelativeValue(): number {\n return scale(this.value, this.min, this.max, 0, 1);\n }\n\n #getEffectiveMarkerStyle(): MarkerStyle {\n if (this.valueMode === `unipolar` && this.markerStyle === `needle`) {\n return `none`;\n }\n return this.markerStyle;\n }\n\n #quantise(value: number): number {\n if (this.stops && this.stops > 1) {\n const range = this.max - this.min;\n const step = range / this.stops;\n return Math.round((value - this.min) / step) * step + this.min;\n }\n return value;\n }\n\n #setValueFromRelative(relativeValue: number, isUserInteraction = false): void {\n const newValue = scale(relativeValue, 0, 1, this.min, this.max);\n const clampedValue = this.#quantise(clamp(newValue, this.min, this.max));\n\n if (clampedValue === this.value)\n return;\n\n const previousValue = this.value;\n this.value = clampedValue;\n this.#isUserInteraction = isUserInteraction;\n\n // Update 'value' lane if it exists\n const valueLane = this.#lanes.get(`value`);\n if (valueLane && valueLane.type === `value`) {\n valueLane.value = this.#getRelativeValue();\n }\n\n // Update data-tooltip for live tooltips\n if (this.hasAttribute(`data-tooltip-live`)) {\n const percentage = Math.round(this.#getRelativeValue() * 100);\n this.setAttribute(`data-tooltip`, `${percentage}%`);\n }\n\n // Fire input event only for user interactions\n if (isUserInteraction) {\n this.dispatchEvent(\n new CustomEvent<RadialInputInputDetail>(`input`, {\n detail: {\n value: this.value,\n valueRelative: this.#getRelativeValue(),\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n // Debounce change event\n this.#scheduleChangeEvent(previousValue);\n } else {\n // Immediate change event for programmatic changes\n this.#fireChangeEvent(previousValue);\n }\n }\n\n #scheduleChangeEvent(previousValue: number): void {\n if (this.#changeDebounceTimer !== undefined) {\n clearTimeout(this.#changeDebounceTimer);\n }\n\n this.#changeDebounceTimer = window.setTimeout(() => {\n this.#fireChangeEvent(previousValue);\n this.#changeDebounceTimer = undefined;\n }, 150);\n }\n\n #fireChangeEvent(previousValue: number): void {\n this.dispatchEvent(\n new CustomEvent<RadialInputChangeDetail>(`change`, {\n detail: {\n value: this.value,\n valueRelative: this.#getRelativeValue(),\n previousValue,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n // Pointer interaction handlers\n #onPointerDown = (event: PointerEvent): void => {\n if (this.disabled)\n return;\n\n event.preventDefault();\n const svg = this.renderRoot.querySelector(`svg`);\n svg?.focus();\n this.#dragging = true;\n this.#dragStartY = event.clientY;\n this.#valueAtDragStart = this.value;\n this.toggleAttribute(`dragging`, true);\n\n if (svg) {\n svg.setPointerCapture(event.pointerId);\n }\n\n window.addEventListener(`pointermove`, this.#onPointerMove);\n window.addEventListener(`pointerup`, this.#onPointerUp);\n window.addEventListener(`pointercancel`, this.#onPointerUp);\n };\n\n #onPointerMove = (event: PointerEvent): void => {\n if (!this.#dragging)\n return;\n\n event.preventDefault();\n\n // Calculate sensitivity: full range = 2× component height\n const sensitivity = this.clientHeight * 2;\n const shiftDivisor = event.shiftKey ? KEY_STEP_DIVISOR : 1;\n const effectiveSensitivity = sensitivity * shiftDivisor;\n\n // Calculate delta (negative because up = increase)\n const deltaY = this.#dragStartY - event.clientY;\n const deltaValue = (deltaY / effectiveSensitivity) * (this.max - this.min);\n const newValue = this.#valueAtDragStart + deltaValue;\n\n const relativeValue = scale(newValue, this.min, this.max, 0, 1);\n this.#setValueFromRelative(clamp(relativeValue, 0, 1), true);\n };\n\n #onPointerUp = (): void => {\n this.#dragging = false;\n this.toggleAttribute(`dragging`, false);\n\n const svg = this.renderRoot.querySelector(`svg`);\n if (svg && `hasPointerCapture` in svg && typeof svg.hasPointerCapture === `function`) {\n if (svg.hasPointerCapture(1)) {\n svg.releasePointerCapture(1);\n }\n }\n\n window.removeEventListener(`pointermove`, this.#onPointerMove);\n window.removeEventListener(`pointerup`, this.#onPointerUp);\n window.removeEventListener(`pointercancel`, this.#onPointerUp);\n };\n\n // Keyboard interaction handler\n #onKeyDown = (event: KeyboardEvent): void => {\n if (this.disabled)\n return;\n\n const shiftDivisor = event.shiftKey ? KEY_STEP_DIVISOR : 1;\n const range = this.max - this.min;\n\n const effectiveStep = resolveStep(range, this.stops, this.step) / shiftDivisor;\n\n let handled = false;\n\n switch (event.key) {\n case `ArrowUp`:\n case `ArrowRight`:\n this.#setValueFromRelative(\n this.#getRelativeValue() + effectiveStep / range,\n true,\n );\n handled = true;\n break;\n\n case `ArrowDown`:\n case `ArrowLeft`:\n this.#setValueFromRelative(\n this.#getRelativeValue() - effectiveStep / range,\n true,\n );\n handled = true;\n break;\n\n case `Home`:\n this.#setValueFromRelative(0, true);\n handled = true;\n break;\n\n case `End`:\n this.#setValueFromRelative(1, true);\n handled = true;\n break;\n }\n\n if (handled) {\n event.preventDefault();\n }\n };\n\n // Rendering helpers\n #renderCircleBorder() {\n const path = generateCircleBorderPath(this.radius, this.flatten, CENTER);\n const borderWidth = this.#getCssVar(`--dial-border-width`, 2);\n const borderColor = getComputedStyle(this).getPropertyValue(`--dial-border-color`).trim() || `var(--surface-4)`;\n return svg`<path class=\"dial-border\" d=\"${path}\" fill=\"none\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\" stroke-linecap=\"round\" vector-effect=\"non-scaling-stroke\" />`;\n }\n\n #renderCircleFill() {\n const path = generateCircleBorderPath(this.radius, this.flatten, CENTER);\n const fill = getComputedStyle(this).getPropertyValue(`--dial-fill`).trim() || `transparent`;\n return svg`<path class=\"dial-fill\" d=\"${path}\" fill=\"${fill}\" />`;\n }\n\n #renderWedge() {\n const relativeValue = this.#getRelativeValue();\n const { startAngle, endAngle, clockwise } = getWedgeAngles(\n relativeValue,\n this.valueMode,\n this.flatten,\n );\n\n const path = generateWedgePath(startAngle, endAngle, this.radius, CENTER, clockwise);\n if (!path)\n return svg``;\n\n return svg`<path class=\"wedge\" d=\"${path}\" />`;\n }\n\n #renderLane(lane: LaneData) {\n let arcPath = ``;\n\n if (lane.type === `fixed`) {\n // Fixed lanes always show full range\n arcPath = generateCircleBorderPath(\n this.radius - lane.offset * this.radius,\n this.flatten,\n CENTER,\n );\n } else if (lane.type === `anchored`) {\n // Anchored lanes: centered on current value, arc width based on lane.value\n const { startAngle, endAngle } = getAnchoredArcAngles(\n this.#getRelativeValue(),\n lane.value,\n this.valueMode,\n this.flatten,\n );\n arcPath = generateArcPath(\n startAngle,\n endAngle,\n this.radius - lane.offset * this.radius,\n CENTER,\n );\n } else {\n // Value lanes follow the value mode\n const { startAngle, endAngle } = getValueArcAngles(\n lane.value,\n this.valueMode,\n this.flatten,\n );\n arcPath = generateArcPath(\n startAngle,\n endAngle,\n this.radius - lane.offset * this.radius,\n CENTER,\n );\n }\n\n if (!arcPath)\n return svg``;\n\n return svg`\n <path \n class=\"lane\" \n data-lane-key=\"${lane.key}\" \n part=\"lane lane-${lane.key}\"\n d=\"${arcPath}\"\n style=\"\n stroke-width: calc(var(--lane-${lane.key}-width, var(--lane-width)) * ${this.radius}%);\n stroke-linecap: round;\n \"\n />\n `;\n }\n\n #getNeedlePath(angle: number, length: number, offset: number): string {\n const startRadius = offset * this.radius;\n const endRadius = length * this.radius;\n\n const start = {\n x: CENTER.x + Math.cos(angle) * startRadius,\n y: CENTER.y + Math.sin(angle) * startRadius,\n };\n const end = {\n x: CENTER.x + Math.cos(angle) * endRadius,\n y: CENTER.y + Math.sin(angle) * endRadius,\n };\n\n return `M ${start.x} ${start.y} L ${end.x} ${end.y}`;\n }\n\n #renderValueMarker(needlePath?: string) {\n const relativeValue = this.#getRelativeValue();\n const { angle, position } = getMarkerPosition(\n relativeValue,\n this.valueMode,\n this.flatten,\n this.radius,\n CENTER,\n );\n\n const markerOffset = this.#getCssVar(`--marker-offset`, 1);\n const markerBorderWidth = this.#getCssVar(`--marker-border-width`, 0);\n const effectiveStyle = this.#getEffectiveMarkerStyle();\n\n if (effectiveStyle === `dot`) {\n const size = this.#getCssVar(`--marker-thickness`, 0.15);\n const r = size * this.radius;\n const dotX = CENTER.x + (position.x - CENTER.x) * markerOffset;\n const dotY = CENTER.y + (position.y - CENTER.y) * markerOffset;\n\n if (markerBorderWidth > 0) {\n return svg`\n <g class=\"value-marker\" part=\"value-marker\">\n <use href=\"#dot-path\" class=\"marker-border\" />\n <use href=\"#dot-path\" class=\"marker-fill\" />\n </g>\n `;\n }\n\n return svg`\n <g class=\"value-marker\" part=\"value-marker\">\n <circle cx=\"${dotX}\" cy=\"${dotY}\" r=\"${r}\" />\n </g>\n `;\n } else if (effectiveStyle === `needle`) {\n if (markerBorderWidth > 0 && needlePath) {\n return svg`\n <g class=\"value-marker\" part=\"value-marker\">\n <use href=\"#needle-path\" class=\"marker-border\" />\n <use href=\"#needle-path\" class=\"marker-fill\" />\n </g>\n `;\n }\n\n const length = this.#getCssVar(`--marker-length`, 0.8);\n const offset = this.#getCssVar(`--marker-offset`, 0.2);\n const path = this.#getNeedlePath(angle, length, offset);\n\n return svg`\n <g class=\"value-marker\" part=\"value-marker\">\n <path d=\"${path}\" />\n </g>\n `;\n }\n }\n\n #renderMidpointMarker() {\n // Only show in bipolar/unipolar modes\n if (this.valueMode !== `bipolar` && this.valueMode !== `unipolar`) {\n return svg``;\n }\n\n const { position } = getMidpointPosition(this.radius, CENTER);\n\n if (this.markerStyle === `dot`) {\n const size = this.#getCssVar(`--midpoint-thickness`, 0.08);\n return svg`\n <g class=\"midpoint-marker\" part=\"midpoint-marker\">\n <circle \n cx=\"${position.x}\" \n cy=\"${position.y}\" \n r=\"${size * this.radius}\" \n />\n </g>\n `;\n } else {\n // Needle style\n const length = this.#getCssVar(`--midpoint-length`, 0.15);\n const offset = this.#getCssVar(`--midpoint-offset`, 0.85);\n\n const angle = -Math.PI / 2; // Top of circle\n const startRadius = offset * this.radius;\n const endRadius = (offset + length) * this.radius;\n\n const start = {\n x: CENTER.x + Math.cos(angle) * startRadius,\n y: CENTER.y + Math.sin(angle) * startRadius,\n };\n const end = {\n x: CENTER.x + Math.cos(angle) * endRadius,\n y: CENTER.y + Math.sin(angle) * endRadius,\n };\n\n return svg`\n <g class=\"midpoint-marker\" part=\"midpoint-marker\">\n <line \n x1=\"${start.x}\" \n y1=\"${start.y}\" \n x2=\"${end.x}\" \n y2=\"${end.y}\" \n />\n </g>\n `;\n }\n }\n\n #getCssVar(name: string, defaultValue: number): number {\n const style = getComputedStyle(this);\n const val = style.getPropertyValue(name).trim();\n if (!val)\n return defaultValue;\n\n // Handle CSS calc() expressions with var(--value-relative)\n if (val.includes(`calc(`)) {\n // Get the current --value-relative value\n const valueRelative = style.getPropertyValue(`--value-relative`).trim();\n if (!valueRelative) {\n // Fallback to default if --value-relative not set\n const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));\n return Number.isNaN(parsed) ? defaultValue : parsed;\n }\n\n // Replace var(--value-relative) with the actual value\n const evalStr = val.replace(/var\\(--value-relative\\)/g, valueRelative);\n\n // Use Function constructor for safe evaluation (no eval)\n try {\n // Create a function that evaluates math expressions\n const fn = new Function(`return ${evalStr.replace(/calc\\s*\\(([^)]+)\\)/g, `$1`)}`);\n const result = fn();\n return typeof result === `number` && !Number.isNaN(result) ? result : defaultValue;\n } catch (e) {\n // Fallback: try to parse just numbers\n const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));\n return Number.isNaN(parsed) ? defaultValue : parsed;\n }\n }\n\n const parsed = Number.parseFloat(val);\n return Number.isNaN(parsed) ? defaultValue : parsed;\n }\n\n // Lifecycle methods\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n // Ensure value lane reflects current value before first render\n // This handles declaratively set initial values\n if (changed.has(`value`) || !this.hasUpdated) {\n const valueLane = this.#lanes.get(`value`);\n if (valueLane && valueLane.type === `value`) {\n valueLane.value = this.#getRelativeValue();\n }\n }\n }\n\n override render(): unknown {\n const lanes = Array.from(this.#lanes.values());\n\n const relativeValue = this.#getRelativeValue();\n const { angle, position } = getMarkerPosition(\n relativeValue,\n this.valueMode,\n this.flatten,\n this.radius,\n CENTER,\n );\n\n const needleLength = this.#getCssVar(`--marker-length`, 0.8);\n const needleOffset = this.#getCssVar(`--marker-offset`, 0.2);\n const markerBorderWidth = this.#getCssVar(`--marker-border-width`, 0);\n const needlePath = markerBorderWidth > 0 ? this.#getNeedlePath(angle, needleLength, needleOffset) : undefined;\n\n const dotSize = this.#getCssVar(`--marker-thickness`, 0.15);\n const dotR = dotSize * this.radius;\n\n const defs = [];\n if (needlePath) {\n defs.push(svg`<path id=\"needle-path\" d=\"${needlePath}\" />`);\n }\n if (markerBorderWidth > 0) {\n defs.push(svg`<circle id=\"dot-path\" cx=\"${position.x}\" cy=\"${position.y}\" r=\"${dotR}\" />`);\n }\n\n return html`\n <svg\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"xMidYMid meet\"\n tabindex=\"${this.disabled ? -1 : 0}\"\n @pointerdown=\"${this.#onPointerDown}\"\n @keydown=\"${this.#onKeyDown}\"\n style=\"cursor: ${this.#dragging ? `ns-resize` : `grab`}\"\n >\n ${defs.length > 0 ? svg`<defs>${defs}</defs>` : ``}\n ${this.#renderCircleFill()}\n ${this.wedge ? this.#renderWedge() : ``}\n ${this.#renderCircleBorder()}\n ${lanes.map(lane => this.#renderLane(lane))}\n ${this.#renderMidpointMarker()}\n ${this.#renderValueMarker(needlePath)}\n </svg>\n `;\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n // Update --radius CSS variable when property changes\n if (changed.has(`radius`)) {\n this.style.setProperty(`--radius`, `${this.radius}%`);\n }\n\n // Update --value-relative CSS variable\n this.style.setProperty(`--value-relative`, this.#getRelativeValue().toString());\n\n // Adjust component height based on flatten to reduce wasted vertical space.\n // The dial is flattened at the top, so we don't need full height.\n if (changed.has(`flatten`)) {\n const markerThickness = this.#getCssVar(`--marker-thickness`, 0.15);\n const radius = this.#getCssVar(`--radius`, 45);\n const markerPadding = Math.round(markerThickness * radius) + 5;\n const viewBoxHeight = Math.round(100 * (1 - this.flatten)) + markerPadding;\n // Set CSS variable to control host height\n this.style.setProperty(`--dial-height`, `${viewBoxHeight}px`);\n // Also update SVG viewBox to match\n const svg = this.renderRoot.querySelector(`svg`);\n if (svg) {\n svg.setAttribute(`viewBox`, `0 0 100 ${viewBoxHeight}`);\n }\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.#changeDebounceTimer !== undefined) {\n clearTimeout(this.#changeDebounceTimer);\n }\n this.#onPointerUp();\n }\n\n override firstUpdated(): void {\n const markerThickness = this.#getCssVar(`--marker-thickness`, 0.15);\n const radius = this.#getCssVar(`--radius`, 45);\n const markerPadding = Math.round(markerThickness * radius);\n const viewBoxHeight = Math.round(100 * (1 - this.flatten)) + markerPadding;\n this.style.setProperty(`--dial-height`, `${viewBoxHeight}px`);\n const svg = this.renderRoot.querySelector(`svg`);\n if (svg) {\n svg.setAttribute(`viewBox`, `0 0 100 ${viewBoxHeight}`);\n }\n }\n\n static override styles = [prominenceStyles, css`\n :host {\n display: inline-block;\n width: 100px;\n height: var(--dial-height, 100px);\n touch-action: none;\n user-select: none;\n tabindex: 0;\n\n /* Dynamic value - set by JS */\n --value-relative: 0;\n\n /* Lane widths (used in stroke-width calculations) */\n --lane-width: 0.16;\n --lane-track-width: 0.08;\n --lane-value-width: 0.12;\n }\n\n :host([disabled]) {\n cursor: not-allowed;\n }\n\n svg {\n width: 100%;\n height: 100%;\n outline: none;\n }\n\n svg:active {\n cursor: grabbing;\n }\n\n svg:focus-visible {\n outline: var(--radial-input-svg-focus-outline, 2px solid var(--accent));\n outline-offset: var(--radial-input-svg-focus-outline-offset, 2px);\n }\n\n .dial-border {\n fill: none;\n stroke: var(--dial-border-color, var(--surface-4));\n stroke-width: var(--dial-border-width, 2px);\n stroke-linecap: round;\n vector-effect: non-scaling-stroke;\n }\n\n .dial-fill {\n fill: var(--dial-fill, transparent);\n }\n\n .wedge {\n fill: var(--wedge-fill, var(--accent));\n fill-opacity: var(--wedge-fill-opacity, 0.25);\n stroke: var(--wedge-border-color, var(--accent));\n stroke-width: var(--wedge-border-width, 0);\n stroke-opacity: var(--wedge-border-opacity, 1);\n vector-effect: non-scaling-stroke;\n }\n\n .lane {\n fill: none;\n stroke-linecap: round;\n stroke-dasharray: var(--lane-dasharray, none);\n stroke-dashoffset: var(--lane-dashoffset, 0);\n vector-effect: non-scaling-stroke;\n }\n\n .lane[data-lane-key='track'] {\n stroke: var(--lane-track-color, var(--surface-4));\n }\n\n .lane[data-lane-key='value'] {\n stroke: var(--lane-value-color, var(--accent));\n }\n\n .lane:not([data-lane-key='track']):not([data-lane-key='value']) {\n stroke: var(--lane-color, var(--accent));\n }\n\n .value-marker path,\n .value-marker .marker-fill {\n stroke: var(--marker-color, var(--accent));\n stroke-width: calc(var(--marker-thickness, 0.15) * var(--radius, 45%));\n stroke-linecap: round;\n stroke-dasharray: var(--marker-dasharray, none);\n stroke-dashoffset: var(--marker-dashoffset, 0);\n vector-effect: non-scaling-stroke;\n }\n\n .value-marker .marker-border {\n stroke: var(--marker-border-color, transparent);\n stroke-width: calc((var(--marker-thickness, 0.15) + var(--marker-border-width, 0) * 2) * var(--radius, 45%));\n stroke-linecap: round;\n vector-effect: non-scaling-stroke;\n }\n\n .value-marker .marker-border {\n fill: none;\n }\n\n .value-marker .marker-fill,\n .value-marker circle {\n fill: var(--marker-color, var(--accent));\n }\n\n .midpoint-marker line {\n stroke: var(--midpoint-color, var(--surface-muted-text));\n stroke-width: var(--midpoint-thickness, 1px);\n stroke-linecap: round;\n vector-effect: non-scaling-stroke;\n }\n\n .midpoint-marker circle {\n fill: var(--midpoint-color, var(--surface-muted-text));\n stroke: var(--midpoint-border-color, transparent);\n stroke-width: var(--midpoint-border-width, 0);\n vector-effect: non-scaling-stroke;\n }\n `];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-radial-input': RadialInputElement;\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"timeline-B62Sz6J_.js","names":["#dragging","#startScreenY","#startHeight","#fireResizeEvent","#isInResizeZone","#pointerId","#resetDrag","#onPointerMove","#onPointerDown","#onPointerUp","#onPointerCancel","customElement","#hasInitializedZoom","#initZoomToFit","#initVerticalZoomToFit","#recomputeTrackLayout","#deriveViewRange","#deriveVerticalZoom","#syncVCanvas","#invalidateCanvas","#cssVarsCache","#getViewportWidth","#vcanvas","#axisHeight","#minPpu","#applyTrackResize","#tabWidth","#getTotalBaseHeight","#getViewportHeight","#getLargestBaseTrackHeight","#getSmallestBaseTrackHeight","#vScrollOffset","#fireSelectionChange","#pendingVerticalZoomToFit","#drawCallback","#onScrollVirtual","#cleanupInteractions","#resizeObserver","#onResize","#syncVCanvasVertical","#positionTabs","#syncVerticalScroll","#prevTickledId","#onVAxisPointerDown","#onVAxisPointerMove","#onVAxisPointerUp","#onTrackResizeMove","#onTrackResizeEnd","#onVZoomInput","#onHZoomInput","#syncVCanvasScroll","customElement","#host","#config","#onTrackSelect","#bpm","#updateFormatLabel","#timeSignatureNumerator","#timeSignatureDenominator"],"sources":["../src/timeline/timeline-draw.ts","../src/timeline/timeline-interactions.ts","../src/timeline/timeline-styles.ts","../src/timeline/timeline-track-tabs.ts","../src/timeline/timeline-element.ts","../src/timeline/timeline-controller.ts"],"sourcesContent":["import type { GridMode, GridResolution, TimelineEvent, TrackLayout, DragTarget, ViewRange, UserScale } from './types.js';\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport type CssVars = Record<string, string>;\n\nexport type Rect = {\n x: number;\n y: number;\n width: number;\n height: number;\n};\n\n// ── Grid resolution ───────────────────────────────────────────────────\n\nconst NICE_STEPS = [1, 2, 5, 10];\n\n/**\n * Dispatch to adaptive or fixed grid mode.\n */\nexport const resolveGrid = (\n viewRange: ViewRange,\n userScale: UserScale,\n viewportWidth: number,\n mode: GridMode = `adaptive`,\n fixedMinorStep?: number,\n ppu?: number,\n): GridResolution => {\n if (mode === `fixed` && fixedMinorStep !== undefined && ppu !== undefined) {\n return resolveFixedGrid(fixedMinorStep, ppu);\n }\n return resolveAdaptiveGrid(viewRange, userScale, viewportWidth);\n};\n\n/**\n * Compute a nice major/minor grid resolution for the current view.\n * Uses a 1-2-5-10 \"nice number\" algorithm.\n */\nconst resolveAdaptiveGrid = (\n viewRange: ViewRange,\n userScale: UserScale,\n viewportWidth: number,\n): GridResolution => {\n const span = viewRange.max - viewRange.min;\n if (span <= 0 || viewportWidth <= 0) {\n return { majorStep: 100, minorDivisions: 4, minorStep: 25 };\n }\n\n // Target ~60-120px between major gridlines\n const densityMultiplier = scaleMultiplier(userScale);\n const targetPx = 80 * densityMultiplier;\n const targetMajors = Math.max(2, Math.floor(viewportWidth / targetPx));\n const rawStep = span / targetMajors;\n\n // Find nearest \"nice\" step\n const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));\n let bestStep = magnitude;\n for (const n of NICE_STEPS) {\n const candidate = n * magnitude;\n if (candidate >= rawStep * 0.8) {\n bestStep = candidate;\n break;\n }\n }\n\n // Minor divisions: 4 for most, 5 for steps of 5/10\n const normalised = bestStep / magnitude;\n const minorDivisions = normalised === 5 || normalised === 10 ? 5 : 4;\n const minorStep = bestStep / minorDivisions;\n\n return { majorStep: bestStep, minorDivisions, minorStep };\n};\n\n/**\n * Fixed grid: user specifies a base minor step in logical units.\n * If the minor step at current zoom would produce lines < 5px apart,\n * double the effective step until they're >= 5px.\n * Major = 4× effective minor.\n */\nconst resolveFixedGrid = (\n baseMinorStep: number,\n ppu: number,\n): GridResolution => {\n const MIN_PX_BETWEEN = 5;\n let effectiveMinor = baseMinorStep;\n\n // Double until minor gridlines are at least MIN_PX_BETWEEN apart\n while (effectiveMinor * ppu < MIN_PX_BETWEEN && effectiveMinor < 1e12) {\n effectiveMinor *= 2;\n }\n\n const minorDivisions = 4;\n const majorStep = effectiveMinor * minorDivisions;\n return { majorStep, minorDivisions, minorStep: effectiveMinor };\n};\n\nconst scaleMultiplier = (scale: UserScale): number => {\n switch (scale) {\n case `x-wide`: return 2.0;\n case `wide`: return 1.5;\n case `normal`: return 1.0;\n case `slim`: return 0.7;\n case `x-slim`: return 0.5;\n default: return 1.0;\n }\n};\n\n// ── Draw: major/minor grid ────────────────────────────────────────────\n\nexport const drawMajorMinorGrid = (\n ctx: CanvasRenderingContext2D,\n region: Rect,\n resolution: GridResolution,\n altMajorBg: boolean,\n vars: CssVars,\n ppu: number,\n): void => {\n const { majorStep, minorStep } = resolution;\n if (majorStep <= 0) return;\n\n const regionEnd = region.x + region.width;\n\n // Draw alternating major background tints\n if (altMajorBg) {\n const altColor = vars[`--timeline-alt-major-bg`] ?? `rgba(255,255,255,0.03)`;\n ctx.fillStyle = altColor;\n const firstMajor = Math.floor(region.x / majorStep);\n for (let i = firstMajor; i * majorStep < regionEnd; i++) {\n if (i % 2 !== 0) {\n const x = i * majorStep * ppu;\n ctx.fillRect(x, region.y, majorStep * ppu, region.height);\n }\n }\n }\n\n // Minor gridlines\n if (minorStep > 0) {\n ctx.strokeStyle = vars[`--timeline-grid-minor-color`] ?? `rgba(255,255,255,0.05)`;\n ctx.lineWidth = parseFloat(vars[`--timeline-grid-minor-width`] ?? `0.5`);\n ctx.beginPath();\n const firstMinor = Math.floor(region.x / minorStep) * minorStep;\n for (let val = firstMinor; val < regionEnd; val += minorStep) {\n // Skip positions that are major gridlines\n if (Math.abs(val % majorStep) < minorStep * 0.01) continue;\n const px = val * ppu;\n ctx.moveTo(px, region.y);\n ctx.lineTo(px, region.y + region.height);\n }\n ctx.stroke();\n }\n\n // Major gridlines\n ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;\n ctx.lineWidth = parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);\n ctx.beginPath();\n const firstMajor = Math.floor(region.x / majorStep) * majorStep;\n for (let val = firstMajor; val < regionEnd; val += majorStep) {\n const px = val * ppu;\n ctx.moveTo(px, region.y);\n ctx.lineTo(px, region.y + region.height);\n }\n ctx.stroke();\n};\n\n// ── Draw: axis labels ─────────────────────────────────────────────────\n\n/**\n * Draw the axis header band. With 2D virtualization the axis is \"sticky\" —\n * it is always drawn at the current vertical scroll position (`scrollTop`)\n * so it appears fixed at the viewport top.\n */\nexport const drawAxis = (\n ctx: CanvasRenderingContext2D,\n region: Rect,\n resolution: GridResolution,\n axisHeight: number,\n formatLabel: (v: number) => string,\n vars: CssVars,\n ppu: number,\n scrollTop = 0,\n): void => {\n const { majorStep } = resolution;\n if (majorStep <= 0 || axisHeight <= 0) return;\n\n // Axis band occupies [scrollTop, scrollTop + axisHeight] in logical Y\n const axisTop = scrollTop;\n const axisBottom = scrollTop + axisHeight;\n\n // Only draw if this tile's region overlaps the axis band\n if (region.y + region.height < axisTop || region.y > axisBottom) return;\n\n const regionEnd = region.x + region.width;\n\n // How far major gridlines extend into the axis band (from the bottom)\n const tickHeight = 6;\n const bgHeight = axisHeight - tickHeight;\n\n // 1. Solid background covers upper portion; leaves bottom strip for gridline ticks.\n const bgColor = vars[`--timeline-bg`] ?? `#111`;\n ctx.fillStyle = bgColor;\n const bgLeft = region.x * ppu;\n ctx.fillRect(bgLeft, axisTop, region.width * ppu, bgHeight);\n\n // 2. Major gridline ticks extending into the axis band\n ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;\n ctx.lineWidth = parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);\n ctx.beginPath();\n const firstMajor = Math.ceil(region.x / majorStep) * majorStep;\n for (let val = firstMajor; val < regionEnd; val += majorStep) {\n const px = val * ppu;\n ctx.moveTo(px, axisTop + bgHeight);\n ctx.lineTo(px, axisBottom);\n }\n ctx.stroke();\n\n // 3. Bottom border\n ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(bgLeft, axisBottom);\n ctx.lineTo(bgLeft + region.width * ppu, axisBottom);\n ctx.stroke();\n\n // 4. Labels to the right of each major gridline\n const labelColor = vars[`--timeline-axis-label-color`] ?? `rgba(255,255,255,0.5)`;\n ctx.fillStyle = labelColor;\n ctx.font = `11px system-ui, sans-serif`;\n ctx.textBaseline = `middle`;\n ctx.textAlign = `left`;\n\n const labelPadding = 4;\n for (let val = firstMajor; val < regionEnd; val += majorStep) {\n const px = val * ppu;\n ctx.fillText(formatLabel(val), px + labelPadding, axisTop + bgHeight / 2);\n }\n\n // Reset textAlign for subsequent draw calls\n ctx.textAlign = `start`;\n};\n\n// ── Draw: track backgrounds ───────────────────────────────────────────\n\nexport type TrackInfo = {\n readonly key: string;\n readonly color?: string;\n};\n\nexport const drawTrackBackgrounds = (\n ctx: CanvasRenderingContext2D,\n region: Rect,\n trackLayout: readonly TrackLayout[],\n tracks: readonly TrackInfo[],\n vars: CssVars,\n ppu: number,\n): void => {\n const bgDefault = vars[`--timeline-track-bg`] ?? `#1a1a1a`;\n const bgAlt = vars[`--timeline-track-bg-alt`] ?? `#1e1e1e`;\n\n for (let i = 0; i < trackLayout.length; i++) {\n const layout = trackLayout[i];\n // Skip if entirely outside the drawn region vertically\n if (layout.y + layout.height < region.y) continue;\n if (layout.y > region.y + region.height) break;\n\n const track = tracks.find(t => t.key === layout.trackKey);\n const bg = track?.color ? track.color + `22` : (i % 2 === 0 ? bgDefault : bgAlt);\n\n ctx.fillStyle = bg;\n ctx.fillRect(region.x * ppu, layout.y, region.width * ppu, layout.height);\n\n // Subtle separator line between tracks\n ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;\n ctx.lineWidth = 0.5;\n ctx.beginPath();\n ctx.moveTo(region.x * ppu, layout.y + layout.height);\n ctx.lineTo((region.x + region.width) * ppu, layout.y + layout.height);\n ctx.stroke();\n }\n};\n\n// ── Draw: event rectangles ────────────────────────────────────────────\n\nexport const drawEventRects = (\n ctx: CanvasRenderingContext2D,\n region: Rect,\n events: readonly TimelineEvent[],\n trackLayout: readonly TrackLayout[],\n selectedIds: ReadonlySet<string>,\n tickledId: string | undefined,\n drag: DragTarget,\n vars: CssVars,\n ppu: number,\n): void => {\n const eventBg = vars[`--timeline-event-bg`] ?? `#3498db`;\n const eventSelectedBg = vars[`--timeline-event-selected-bg`] ?? `#5dade2`;\n const eventTickledBg = vars[`--timeline-event-tickled-bg`] ?? `#444`;\n const eventLabelColor = vars[`--timeline-event-label-color`] ?? `#fff`;\n const radius = parseFloat(vars[`--timeline-event-radius`] ?? `4`);\n\n const regionEnd = region.x + region.width;\n const padding = 3; // vertical padding inside track\n\n for (const evt of events) {\n // Skip events outside horizontal view\n if (evt.end < region.x || evt.start > regionEnd) continue;\n\n const layout = trackLayout.find(l => l.trackKey === evt.trackKey);\n if (!layout) continue;\n\n // Skip events outside vertical region\n if (layout.y + layout.height < region.y) continue;\n if (layout.y > region.y + region.height) continue;\n\n // Compute provisional position during drag\n let isDragging = false;\n\n if (drag.kind === `event-move` && drag.eventId === evt.id) {\n isDragging = true;\n } else if (drag.kind === `event-start` && drag.eventId === evt.id) {\n isDragging = true;\n } else if (drag.kind === `event-end` && drag.eventId === evt.id) {\n isDragging = true;\n }\n\n const x = evt.start * ppu;\n const w = (evt.end - evt.start) * ppu;\n const y = layout.y + padding;\n const h = layout.height - padding * 2;\n\n if (w < 1) continue;\n\n // Choose fill color\n const isSelected = selectedIds.has(evt.id);\n const isTickled = tickledId === evt.id;\n let fill: string;\n if (isSelected) {\n fill = eventSelectedBg;\n } else if (isTickled) {\n fill = eventTickledBg;\n } else {\n fill = evt.color ?? eventBg;\n }\n\n // Draw rounded rect\n ctx.fillStyle = fill;\n ctx.beginPath();\n drawRoundRect(ctx, x, y, w, h, Math.min(radius, w / 2, h / 2));\n ctx.fill();\n\n // Drag outline\n if (isDragging) {\n ctx.strokeStyle = `#fff`;\n ctx.lineWidth = 1.5;\n ctx.beginPath();\n drawRoundRect(ctx, x, y, w, h, Math.min(radius, w / 2, h / 2));\n ctx.stroke();\n }\n\n // Selection outline\n if (isSelected && !isDragging) {\n ctx.strokeStyle = eventSelectedBg;\n ctx.lineWidth = 1;\n ctx.beginPath();\n drawRoundRect(ctx, x, y, w, h, Math.min(radius, w / 2, h / 2));\n ctx.stroke();\n }\n\n // Label (only if wide enough in pixels)\n if (w >= 40 && evt.label) {\n ctx.fillStyle = eventLabelColor;\n ctx.font = `11px system-ui, sans-serif`;\n ctx.textBaseline = `middle`;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x + 4, y, w - 8, h);\n ctx.clip();\n ctx.fillText(evt.label, x + 6, y + h / 2);\n ctx.restore();\n }\n }\n};\n\n// ── Draw: marquee selection rectangle ─────────────────────────────────\n\nexport const drawMarquee = (\n ctx: CanvasRenderingContext2D,\n drag: DragTarget,\n ppu: number,\n): void => {\n if (drag.kind !== `marquee`) return;\n\n const x1 = Math.min(drag.startLogicalX, drag.currentLogicalX) * ppu;\n const x2 = Math.max(drag.startLogicalX, drag.currentLogicalX) * ppu;\n const y1 = Math.min(drag.startY, drag.currentY);\n const y2 = Math.max(drag.startY, drag.currentY);\n\n ctx.fillStyle = `rgba(100, 150, 255, 0.15)`;\n ctx.fillRect(x1, y1, x2 - x1, y2 - y1);\n\n ctx.strokeStyle = `rgba(100, 150, 255, 0.6)`;\n ctx.lineWidth = 1;\n ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\nconst drawRoundRect = (\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number,\n): void => {\n r = Math.max(0, r);\n ctx.moveTo(x + r, y);\n ctx.arcTo(x + w, y, x + w, y + h, r);\n ctx.arcTo(x + w, y + h, x, y + h, r);\n ctx.arcTo(x, y + h, x, y, r);\n ctx.arcTo(x, y, x + w, y, r);\n ctx.closePath();\n};\n\n// ── CSS variable resolution helper ────────────────────────────────────\n\nconst TIMELINE_CSS_VARS = [\n `--timeline-bg`,\n `--timeline-track-bg`,\n `--timeline-track-bg-alt`,\n `--timeline-alt-major-bg`,\n `--timeline-grid-major-color`,\n `--timeline-grid-minor-color`,\n `--timeline-grid-major-width`,\n `--timeline-grid-minor-width`,\n `--timeline-event-bg`,\n `--timeline-event-selected-bg`,\n `--timeline-event-tickled-bg`,\n `--timeline-event-label-color`,\n `--timeline-event-radius`,\n `--timeline-axis-label-color`,\n] as const;\n\nexport const resolveCssVars = (host: HTMLElement): CssVars => {\n const computed = getComputedStyle(host);\n const vars: CssVars = {};\n for (const v of TIMELINE_CSS_VARS) {\n vars[v] = computed.getPropertyValue(v).trim();\n }\n return vars;\n};\n","import type { IxfxTimelineElement } from './timeline-element.js';\nimport type { TimelineEvent, TrackLayout, DragTarget, TimelineEventMoveDetail, TimelineEventResizeDetail, TimelineSelectionChangeDetail, GridResolution } from './types.js';\nimport type { ViewRange } from './types.js';\nimport { resolveGrid } from './timeline-draw.js';\n\n// Minimal type for the x-vcanvas element\ntype VCanvasElement = HTMLElement & {\n convertFromViewportToCanvas(x: number, y: number): { x: number; y: number };\n viewportWidth: number;\n viewportHeight: number;\n logicalScrollTop: number;\n invalidateAll(): void;\n invalidateRegion(x: number, y: number, w: number, h: number): void;\n};\n\n/** Hit zone near the edge of an event (px in screen space) */\nconst EDGE_HIT_PX = 8;\n\n// ── Coordinate conversion ─────────────────────────────────────────────\n\n/**\n * Convert a vcanvas logical (pixel) coordinate to timeline logical units.\n * vcanvas pixel coordinates are in the 1:1 pixel space; divide by ppu then add min.\n */\nconst canvasToTimeline = (canvasX: number, ppu: number, min: number): number =>\n canvasX / ppu + min;\n\n/**\n * Convert screen-space pixel distance to timeline logical units.\n * Used for edge hit threshold: how many logical units does EDGE_HIT_PX correspond to.\n */\nconst pxToLogicalUnits = (ppu: number): number =>\n ppu > 0 ? 1 / ppu : 1;\n\n// ── Hit testing ───────────────────────────────────────────────────────\n\ntype HitResult =\n | { kind: 'none' }\n | { kind: 'event-move'; eventId: string; trackKey: string; offsetX: number }\n | { kind: 'event-start'; eventId: string }\n | { kind: 'event-end'; eventId: string }\n | { kind: 'empty'; logicalX: number; y: number; trackKey: string | undefined };\n\nconst hitTest = (\n logicalX: number,\n y: number,\n events: readonly TimelineEvent[],\n trackLayout: readonly TrackLayout[],\n ppu: number,\n): HitResult => {\n // Find which track was clicked\n const track = trackLayout.find(tl => y >= tl.y && y < tl.y + tl.height);\n if (!track) {\n return { kind: `empty`, logicalX, y, trackKey: undefined };\n }\n\n // Edge threshold in logical units\n const edgeThreshold = EDGE_HIT_PX * pxToLogicalUnits(ppu);\n const trackEvents = events.filter(e => e.trackKey === track.trackKey);\n\n for (const evt of trackEvents) {\n if (logicalX < evt.start || logicalX > evt.end) continue;\n\n // Within event — check edges\n if (logicalX <= evt.start + edgeThreshold) {\n return { kind: `event-start`, eventId: evt.id };\n }\n if (logicalX >= evt.end - edgeThreshold) {\n return { kind: `event-end`, eventId: evt.id };\n }\n return {\n kind: `event-move`,\n eventId: evt.id,\n trackKey: track.trackKey,\n offsetX: logicalX - evt.start,\n };\n }\n\n return { kind: `empty`, logicalX, y, trackKey: track.trackKey };\n};\n\n// ── Find track at Y position ──────────────────────────────────────────\n\nconst trackAtY = (y: number, trackLayout: readonly TrackLayout[]): string | undefined => {\n const track = trackLayout.find(tl => y >= tl.y && y < tl.y + tl.height);\n return track?.trackKey;\n};\n\n// ── Axis zoom helpers ─────────────────────────────────────────────────\n\n/** Sensitivity: how many pixels of vertical drag = 1 e-fold of zoom */\nconst ZOOM_SENSITIVITY_PX = 100;\n\n/** Total pointer movement (px) before an axis drag activates */\nexport const DRAG_ACTIVATION_PX = 5;\n\n/** Per-axis deadzone (px) — small off-axis movement is ignored */\nexport const AXIS_DEADZONE_PX = 4;\n\n/**\n * Subtract a deadzone from a value.\n * Returns 0 if |value| <= deadzone, otherwise value shifted toward zero by deadzone.\n */\nexport const applyDeadzone = (value: number, deadzone: number): number =>\n Math.abs(value) > deadzone ? value - Math.sign(value) * deadzone : 0;\n\n/**\n * Compute new ppu + viewMin from an axis-zoom drag.\n * Vertical delta (dy) controls zoom (up = zoom in, down = zoom out), anchored at `anchorUnit`.\n * Horizontal delta (dx) controls pan.\n *\n * Returns { ppu, viewMin } — the caller uses host.setZoomPan() to apply.\n */\nconst computeAxisZoom = (\n startViewMin: number,\n startPpu: number,\n anchorUnit: number,\n dx: number,\n dy: number,\n): { ppu: number; viewMin: number } => {\n // Zoom: exponential scale factor from vertical drag (drag up = zoom in)\n const zoomFactor = Math.exp(-dy / ZOOM_SENSITIVITY_PX);\n const newPpu = startPpu * zoomFactor;\n\n // Anchor: the anchorUnit should stay at the same fraction of the viewport.\n // At startPpu, its screen position was: (anchorUnit - startViewMin) * startPpu\n // At newPpu, we want the same screen position, so:\n // (anchorUnit - newMin) * newPpu = (anchorUnit - startViewMin) * startPpu\n // newMin = anchorUnit - (anchorUnit - startViewMin) * startPpu / newPpu\n const anchorScreenX = (anchorUnit - startViewMin) * startPpu;\n let newMin = anchorUnit - anchorScreenX / newPpu;\n\n // Pan: horizontal drag shifts the view in screen pixels, convert to timeline units\n if (newPpu > 0) {\n newMin += -dx / newPpu;\n }\n\n // Don't clamp here — host.setZoomPan() handles clamping via #deriveViewRange\n return { ppu: newPpu, viewMin: newMin };\n};\n\n/**\n * Compute new vertical view range from a v-axis-zoom drag.\n * Horizontal delta (dx) controls zoom (right = zoom in, left = zoom out),\n * anchored at `anchorBaseY` (a position in base-height units).\n * Vertical delta (dy) controls pan (drag down = scroll down through tracks).\n *\n * Returns { vMin, vMax } — the caller uses host.setVerticalZoomPan() to apply.\n */\nexport const computeVerticalAxisZoom = (\n startVMin: number,\n startVMax: number,\n startVScale: number,\n anchorBaseY: number,\n dx: number,\n dy: number,\n): { vMin: number; vMax: number } => {\n const startSpan = startVMax - startVMin;\n\n // Zoom: exponential scale factor from horizontal drag (right = zoom in)\n const zoomFactor = Math.exp(-dx / ZOOM_SENSITIVITY_PX);\n const newSpan = startSpan * zoomFactor;\n\n // Anchor: keep anchorBaseY at the same visual fraction\n const anchorFrac = startSpan > 0\n ? (anchorBaseY - startVMin) / startSpan\n : 0.5;\n let newMin = anchorBaseY - anchorFrac * newSpan;\n\n // Pan: vertical drag shifts the view in screen pixels, convert to base-height units\n if (startVScale > 0) {\n newMin += dy / startVScale;\n }\n\n // Don't clamp here — host.setVerticalZoomPan() handles clamping via #deriveVerticalZoom\n return { vMin: newMin, vMax: newMin + newSpan };\n};\n\n// ── Attach interactions ───────────────────────────────────────────────\n\n/**\n * Attaches pointer and keyboard interaction handlers to the timeline.\n * Returns a cleanup function.\n */\nexport const attachTimelineInteractions = (\n host: IxfxTimelineElement,\n vcanvasEl: VCanvasElement,\n): (() => void) => {\n const ac = new AbortController();\n const signal = ac.signal;\n\n // ── Pointer down on vcanvas ───────────────────────────────────────\n vcanvasEl.addEventListener(`pointerdown`, (e: PointerEvent) => {\n const ppu = host.pxPerUnit;\n const canvasCoord = vcanvasEl.convertFromViewportToCanvas(e.offsetX, e.offsetY);\n // canvasCoord.x is in vcanvas logical space which is pure timeline pixel space\n const lx = canvasToTimeline(canvasCoord.x, ppu, host.min);\n const ly = canvasCoord.y;\n\n // Check if click is in the axis header band → start axis-zoom\n // Axis is \"sticky\" at the current logicalScrollTop\n const axisTop = vcanvasEl.logicalScrollTop;\n if (ly >= axisTop && ly < axisTop + host.axisHeight) {\n vcanvasEl.setPointerCapture(e.pointerId);\n host._drag = {\n kind: `axis-zoom`,\n anchorUnit: lx,\n startScreenX: e.screenX,\n startScreenY: e.screenY,\n startViewMin: host._viewRange.min,\n startPpu: ppu,\n };\n return;\n }\n\n const hit = hitTest(lx, ly, host._internalEvents, host._trackLayout, ppu);\n\n if (e.shiftKey && hit.kind === `event-move`) {\n // SHIFT+click toggles selection\n toggleSelection(host, hit.eventId);\n return;\n }\n\n if (hit.kind === `event-move`) {\n // Start move drag\n vcanvasEl.setPointerCapture(e.pointerId);\n host._drag = {\n kind: `event-move`,\n eventId: hit.eventId,\n originTrackKey: hit.trackKey,\n offsetX: hit.offsetX,\n };\n return;\n }\n\n if (hit.kind === `event-start`) {\n vcanvasEl.setPointerCapture(e.pointerId);\n host._drag = { kind: `event-start`, eventId: hit.eventId };\n return;\n }\n\n if (hit.kind === `event-end`) {\n vcanvasEl.setPointerCapture(e.pointerId);\n host._drag = { kind: `event-end`, eventId: hit.eventId };\n return;\n }\n\n if (hit.kind === `empty`) {\n // Start marquee or clear selection\n vcanvasEl.setPointerCapture(e.pointerId);\n host._drag = {\n kind: `marquee`,\n startLogicalX: lx,\n startY: ly,\n currentLogicalX: lx,\n currentY: ly,\n };\n return;\n }\n }, { signal });\n\n // ── Pointer move ──────────────────────────────────────────────────\n vcanvasEl.addEventListener(`pointermove`, (e: PointerEvent) => {\n const drag = host._drag;\n if (drag.kind === `none`) {\n // Tickle (hover) detection\n handleHover(host, vcanvasEl, e);\n return;\n }\n\n const ppu = host.pxPerUnit;\n const canvasCoord = vcanvasEl.convertFromViewportToCanvas(e.offsetX, e.offsetY);\n // canvasCoord.x is in vcanvas logical space which is pure timeline pixel space\n const lx = canvasToTimeline(canvasCoord.x, ppu, host.min);\n const ly = canvasCoord.y;\n\n if (drag.kind === `axis-zoom`) {\n const rawDx = e.screenX - drag.startScreenX;\n const rawDy = e.screenY - drag.startScreenY;\n\n // Activation threshold — don't start until enough total movement\n if (Math.abs(rawDx) + Math.abs(rawDy) < DRAG_ACTIVATION_PX) return;\n\n // Per-axis deadzone — ignore small off-axis movement\n const dx = applyDeadzone(rawDx, AXIS_DEADZONE_PX);\n const dy = applyDeadzone(rawDy, AXIS_DEADZONE_PX);\n\n const result = computeAxisZoom(\n drag.startViewMin, drag.startPpu,\n drag.anchorUnit,\n dx, dy,\n );\n host.setZoomPan(result.viewMin, result.ppu);\n return;\n }\n\n if (drag.kind === `marquee`) {\n host._drag = { ...drag, currentLogicalX: lx, currentY: ly };\n vcanvasEl.invalidateAll();\n return;\n }\n\n if (drag.kind === `event-move`) {\n const evt = host._internalEvents.find(ev => ev.id === drag.eventId);\n if (!evt) return;\n const duration = evt.end - evt.start;\n const rawLx = lx;\n const rawLy = ly;\n const newStart = lx - drag.offsetX;\n const newEnd = newStart + duration;\n const newTrackKey = trackAtY(ly, host._trackLayout) ?? evt.trackKey;\n\n const ppu = host.pxPerUnit;\n const grid: GridResolution = resolveGrid(\n host._viewRange,\n host.userScale,\n vcanvasEl.viewportWidth,\n host.gridMode,\n host.minorStep,\n ppu,\n );\n\n const snapped = host.processDrag(\n drag.eventId,\n rawLx,\n rawLy,\n newStart,\n newEnd,\n newTrackKey,\n drag.originTrackKey,\n grid,\n );\n\n host._internalEvents = host._internalEvents.map(ev =>\n ev.id === drag.eventId\n ? { ...ev, start: snapped.start, end: snapped.end, trackKey: snapped.trackKey }\n : ev\n );\n vcanvasEl.invalidateAll();\n return;\n }\n\n if (drag.kind === `event-start`) {\n const evt = host._internalEvents.find(ev => ev.id === drag.eventId);\n if (!evt) return;\n const newStart = Math.min(lx, evt.end - 1);\n host._internalEvents = host._internalEvents.map(ev =>\n ev.id === drag.eventId ? { ...ev, start: newStart } : ev\n );\n vcanvasEl.invalidateAll();\n return;\n }\n\n if (drag.kind === `event-end`) {\n const evt = host._internalEvents.find(ev => ev.id === drag.eventId);\n if (!evt) return;\n const newEnd = Math.max(lx, evt.start + 1);\n host._internalEvents = host._internalEvents.map(ev =>\n ev.id === drag.eventId ? { ...ev, end: newEnd } : ev\n );\n vcanvasEl.invalidateAll();\n return;\n }\n }, { signal });\n\n // ── Pointer up ────────────────────────────────────────────────────\n vcanvasEl.addEventListener(`pointerup`, (e: PointerEvent) => {\n const drag = host._drag;\n vcanvasEl.releasePointerCapture(e.pointerId);\n\n if (drag.kind === `axis-zoom`) {\n // Final zoom/pan is already applied continuously in pointermove\n host._drag = { kind: `none` };\n return;\n }\n\n if (drag.kind === `marquee`) {\n // Compute marquee selection\n const x1 = Math.min(drag.startLogicalX, drag.currentLogicalX);\n const x2 = Math.max(drag.startLogicalX, drag.currentLogicalX);\n const y1 = Math.min(drag.startY, drag.currentY);\n const y2 = Math.max(drag.startY, drag.currentY);\n\n // Check if it was just a click (no drag distance)\n if (Math.abs(x2 - x1) < 2 && Math.abs(y2 - y1) < 2) {\n // Click on empty space — clear selection\n host._selectedIds = new Set();\n fireSelectionChange(host);\n } else {\n // Select events intersecting the marquee\n const selected = new Set<string>();\n for (const evt of host._internalEvents) {\n const layout = host._trackLayout.find(l => l.trackKey === evt.trackKey);\n if (!layout) continue;\n const evtTop = layout.y + 3;\n const evtBottom = layout.y + layout.height - 3;\n\n // Intersection test\n if (evt.end >= x1 && evt.start <= x2 && evtBottom >= y1 && evtTop <= y2) {\n selected.add(evt.id);\n }\n }\n host._selectedIds = selected;\n fireSelectionChange(host);\n }\n\n host._drag = { kind: `none` };\n vcanvasEl.invalidateAll();\n return;\n }\n\n if (drag.kind === `event-move`) {\n const evt = host._internalEvents.find(ev => ev.id === drag.eventId);\n if (evt) {\n host.dispatchEvent(\n new CustomEvent<TimelineEventMoveDetail>(`event-move`, {\n detail: {\n eventId: evt.id,\n fromTrackKey: drag.originTrackKey,\n toTrackKey: evt.trackKey,\n start: evt.start,\n end: evt.end,\n },\n bubbles: true,\n composed: true,\n })\n );\n }\n host._drag = { kind: `none` };\n return;\n }\n\n if (drag.kind === `event-start` || drag.kind === `event-end`) {\n const evt = host._internalEvents.find(ev => ev.id === drag.eventId);\n if (evt) {\n host.dispatchEvent(\n new CustomEvent<TimelineEventResizeDetail>(`event-resize`, {\n detail: {\n eventId: evt.id,\n start: evt.start,\n end: evt.end,\n },\n bubbles: true,\n composed: true,\n })\n );\n }\n host._drag = { kind: `none` };\n return;\n }\n\n host._drag = { kind: `none` };\n }, { signal });\n\n // ── Pointer leave (clear tickle) ──────────────────────────────────\n vcanvasEl.addEventListener(`pointerleave`, () => {\n if (host._tickledId !== undefined) {\n host._tickledId = undefined;\n vcanvasEl.invalidateAll();\n }\n }, { signal });\n\n return () => ac.abort();\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\nconst handleHover = (\n host: IxfxTimelineElement,\n vcanvasEl: VCanvasElement,\n e: PointerEvent,\n): void => {\n const ppu = host.pxPerUnit;\n const canvasCoord = vcanvasEl.convertFromViewportToCanvas(e.offsetX, e.offsetY);\n // canvasCoord.x is in vcanvas logical space which is pure timeline pixel space\n const lx = canvasToTimeline(canvasCoord.x, ppu, host.min);\n const ly = canvasCoord.y;\n\n // Axis band: show appropriate cursor (sticky at logicalScrollTop)\n const axisTop = vcanvasEl.logicalScrollTop;\n if (ly >= axisTop && ly < axisTop + host.axisHeight) {\n vcanvasEl.style.cursor = `col-resize`;\n // Clear tickle if any\n if (host._tickledId !== undefined) {\n host._tickledId = undefined;\n vcanvasEl.invalidateAll();\n }\n return;\n }\n\n const hit = hitTest(lx, ly, host._internalEvents, host._trackLayout, ppu);\n\n let newTickledId: string | undefined;\n if (hit.kind === `event-move` || hit.kind === `event-start` || hit.kind === `event-end`) {\n newTickledId = hit.eventId;\n }\n\n // Update cursor\n if (hit.kind === `event-start` || hit.kind === `event-end`) {\n vcanvasEl.style.cursor = `ew-resize`;\n } else if (hit.kind === `event-move`) {\n vcanvasEl.style.cursor = `grab`;\n } else {\n vcanvasEl.style.cursor = `crosshair`;\n }\n\n if (newTickledId !== host._tickledId) {\n host._tickledId = newTickledId;\n vcanvasEl.invalidateAll();\n }\n};\n\nconst toggleSelection = (host: IxfxTimelineElement, eventId: string): void => {\n const next = new Set(host._selectedIds);\n if (next.has(eventId)) {\n next.delete(eventId);\n } else {\n next.add(eventId);\n }\n host._selectedIds = next;\n fireSelectionChange(host);\n host.getVCanvas()?.invalidateAll();\n};\n\nconst fireSelectionChange = (host: IxfxTimelineElement): void => {\n host.dispatchEvent(\n new CustomEvent<TimelineSelectionChangeDetail>(`selection-change`, {\n detail: { selectedIds: [...host._selectedIds] },\n bubbles: true,\n composed: true,\n })\n );\n};\n","import { css } from 'lit';\n\nexport const timelineStyles = css`\n :host {\n /* Layout */\n --timeline-axis-height: 24px;\n --timeline-tab-width: 120px;\n\n /* Backgrounds */\n --timeline-bg: var(--content, #111);\n --timeline-track-bg: var(--surface-2);\n --timeline-track-bg-alt: var(--surface-3);\n --timeline-alt-major-bg: color-mix(in oklch, var(--surface-3) 40%, transparent);\n --timeline-meta-bg: var(--surface-2);\n\n /* Grid */\n --timeline-grid-major-color: var(--surface-5);\n --timeline-grid-minor-color: var(--surface-4);\n --timeline-grid-major-width: 1px;\n --timeline-grid-minor-width: 0.5px;\n\n /* Events */\n --timeline-event-bg: var(--accent);\n --timeline-event-selected-bg: color-mix(in oklch, var(--accent) 80%, white);\n --timeline-event-tickled-bg: var(--surface-6);\n --timeline-event-label-color: var(--accent-text);\n --timeline-event-radius: var(--radius-s, 4px);\n\n /* Tabs */\n --timeline-tab-bg: var(--surface-3);\n --timeline-tab-text: var(--surface-3-text);\n --timeline-tab-selected-bg: var(--accent);\n\n /* Axis */\n --timeline-axis-label-color: var(--surface-muted-text);\n\n /* Resizer */\n --timeline-resizer-color: var(--surface-5);\n\n display: grid;\n grid-template-rows: 1fr auto auto auto;\n background: var(--timeline-bg);\n overflow: hidden;\n position: relative;\n }\n\n /* ── Content wrapper ───────────────────────────────────────────────── */\n\n .content-wrapper {\n display: grid;\n grid-template-columns: 1fr auto;\n overflow: hidden;\n min-height: 0;\n }\n\n /* ── Vertical zoom (right sidebar) ─────────────────────────────────── */\n\n .v-zoom {\n padding: 6px 4px;\n padding-top: var(--timeline-axis-height);\n display: flex;\n align-items: stretch;\n width: 30px;\n box-sizing: content-box;\n }\n\n .v-zoom ixfx-range-multi {\n height: 100%;\n width: 100%;\n }\n\n /* ── Content area ──────────────────────────────────────────────────── */\n\n .content-area {\n position: relative;\n display: grid;\n grid-template-columns: var(--timeline-tab-width) 1fr;\n overflow: hidden;\n min-height: 0;\n }\n\n /* ── Tab column (sits beside vcanvas) ────────────────────────────── */\n\n .tab-column {\n position: relative;\n overflow: hidden;\n z-index: 2;\n }\n\n .tab-column ixfx-timeline-track-tab {\n position: absolute;\n left: 0;\n width: 100%;\n box-sizing: border-box;\n pointer-events: auto;\n }\n\n /* ── Vertical axis interaction zone ─────────────────────────────────── */\n\n .v-axis-zone {\n position: absolute;\n top: 0;\n left: 0;\n width: var(--timeline-tab-width);\n height: 100%;\n cursor: ns-resize;\n z-index: 3;\n }\n\n /* ── VCanvas ───────────────────────────────────────────────────────── */\n\n .content-area x-vcanvas {\n width: 100%;\n height: 100%;\n display: block;\n --scrollbar-width: none;\n }\n\n /* ── Horizontal zoom ───────────────────────────────────────────────── */\n\n .h-zoom {\n padding: 4px 8px;\n padding-left: var(--timeline-tab-width);\n padding-right: 38px; /* align with v-zoom column (30px + 2×4px padding) */\n }\n\n .h-zoom ixfx-range-multi {\n width: 100%;\n }\n\n /* ── Meta area ─────────────────────────────────────────────────────── */\n\n .meta-resizer {\n display: none;\n height: 6px;\n cursor: ns-resize;\n background: var(--timeline-resizer-color);\n }\n\n .meta-resizer.visible {\n display: block;\n }\n\n .meta-area {\n display: none;\n background: var(--timeline-meta-bg);\n overflow: hidden;\n }\n\n .meta-area.visible {\n display: block;\n }\n`;\n","import { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport type { TimelineTrackResizeDetail } from './types.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\n/** Minimum track height in px */\nconst MIN_TRACK_HEIGHT = 24;\n\n/** How close to the bottom edge the pointer must be (px) to trigger resize */\nconst EDGE_ZONE_PX = 6;\n\n/**\n * A single track tab in the timeline's left margin.\n * Displays the track label, optional drag handle for reordering,\n * and handles vertical resize via bottom-edge drag.\n *\n * Fires:\n * - `track-resize-move` (composed, bubbles) — live preview during drag\n * - `track-resize` (composed, bubbles) — final value on pointerup\n *\n * Both carry `TimelineTrackResizeDetail` in their `detail`.\n */\n@customElement(`ixfx-timeline-track-tab`)\nexport class TimelineTrackTabElement extends LitElement {\n static override styles = css`\n :host {\n display: flex;\n align-items: center;\n background: color-mix(in oklch, var(--timeline-tab-bg, #2a2a2a) 70%, transparent);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: var(--timeline-tab-text, #ccc);\n font-size: 0.75rem;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n cursor: pointer;\n user-select: none;\n border-bottom: 1px solid var(--timeline-grid-major-color, rgba(255,255,255,0.12));\n box-sizing: border-box;\n position: relative;\n }\n\n :host([selected]) {\n background: color-mix(in oklch, var(--timeline-tab-selected-bg, #3498db) 75%, transparent);\n color: white;\n }\n\n .handle {\n display: none;\n cursor: grab;\n padding: 0 4px;\n opacity: 0.5;\n font-size: 0.65rem;\n }\n\n :host([reorderable]) .handle {\n display: block;\n }\n\n .label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n padding: 0 8px;\n }\n `;\n\n @property({ type: String }) label = ``;\n @property({ type: String, attribute: `track-key` }) trackKey = ``;\n @property({ type: Boolean, reflect: true }) selected = false;\n @property({ type: Boolean, reflect: true }) reorderable = false;\n @property({ type: Boolean, reflect: true }) resizable = false;\n\n // ── Private drag state (not reactive — no re-render needed) ─────────\n #dragging = false;\n #startScreenY = 0;\n #startHeight = 0;\n #pointerId: number | undefined = undefined;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(`pointermove`, this.#onPointerMove);\n this.addEventListener(`pointerdown`, this.#onPointerDown);\n this.addEventListener(`pointerup`, this.#onPointerUp);\n this.addEventListener(`pointercancel`, this.#onPointerCancel);\n this.addEventListener(`lostpointercapture`, this.#onPointerCancel);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`pointermove`, this.#onPointerMove);\n this.removeEventListener(`pointerdown`, this.#onPointerDown);\n this.removeEventListener(`pointerup`, this.#onPointerUp);\n this.removeEventListener(`pointercancel`, this.#onPointerCancel);\n this.removeEventListener(`lostpointercapture`, this.#onPointerCancel);\n this.#dragging = false;\n }\n\n protected override render() {\n return html`\n <span class=\"handle\">\\u2630</span>\n <span class=\"label\">${this.label}</span>\n `;\n }\n\n // ── Helpers ──────────────────────────────────────────────────────────\n\n /** Is the pointer within the bottom edge zone? */\n #isInResizeZone(e: PointerEvent): boolean {\n if (!this.resizable) return false;\n const rect = this.getBoundingClientRect();\n return e.clientY >= rect.bottom - EDGE_ZONE_PX;\n }\n\n #fireResizeEvent(eventName: string, height: number): void {\n this.dispatchEvent(\n new CustomEvent<TimelineTrackResizeDetail>(eventName, {\n detail: { trackKey: this.trackKey, height },\n bubbles: true,\n composed: true,\n })\n );\n }\n\n // ── Pointer handlers ────────────────────────────────────────────────\n\n #onPointerMove = (e: PointerEvent): void => {\n if (this.#dragging) {\n // During drag: compute new height from delta\n const dy = e.screenY - this.#startScreenY;\n const newHeight = Math.max(MIN_TRACK_HEIGHT, this.#startHeight + dy);\n this.#fireResizeEvent(`track-resize-move`, newHeight);\n return;\n }\n\n // Hover: show ns-resize cursor when near bottom edge\n if (this.#isInResizeZone(e)) {\n this.style.cursor = `ns-resize`;\n } else {\n this.style.cursor = ``;\n }\n };\n\n #onPointerDown = (e: PointerEvent): void => {\n if (!this.#isInResizeZone(e)) return;\n\n // Prevent the click handler on the parent from firing (track-select)\n e.stopPropagation();\n e.preventDefault();\n\n this.#dragging = true;\n this.#startScreenY = e.screenY;\n this.#startHeight = this.offsetHeight;\n this.#pointerId = e.pointerId;\n this.setPointerCapture(e.pointerId);\n };\n\n #onPointerUp = (e: PointerEvent): void => {\n if (!this.#dragging) return;\n\n const dy = e.screenY - this.#startScreenY;\n const finalHeight = Math.max(MIN_TRACK_HEIGHT, this.#startHeight + dy);\n\n this.#fireResizeEvent(`track-resize`, finalHeight);\n this.#resetDrag(e.pointerId);\n };\n\n #onPointerCancel = (_e: Event): void => {\n if (!this.#dragging) return;\n this.#resetDrag(this.#pointerId);\n };\n\n #resetDrag(pointerId?: number): void {\n this.#dragging = false;\n this.#startScreenY = 0;\n this.#startHeight = 0;\n if (pointerId !== undefined) {\n try { this.releasePointerCapture(pointerId); } catch { /* already released */ }\n }\n this.#pointerId = undefined;\n this.style.cursor = ``;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-timeline-track-tab': TimelineTrackTabElement;\n }\n}\n","import type { PropertyValues } from 'lit';\nimport type { CssVars, Rect } from './timeline-draw.js';\nimport type {\n DragSnappingFn,\n DragTarget,\n GridMode,\n GridResolution,\n TimelineElementEventMap,\n TimelineEvent,\n TimelineSelectionChangeDetail,\n TimelineTickleChangeDetail,\n TimelineTrack,\n TimelineTrackResizeDetail,\n TimelineTrackSelectDetail,\n TrackHeightMode,\n TrackLayout,\n UserScale,\n ViewRange,\n} from './types.js';\nimport { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport {\n drawAxis,\n drawEventRects,\n drawMajorMinorGrid,\n drawMarquee,\n drawTrackBackgrounds,\n resolveCssVars,\n resolveGrid,\n} from './timeline-draw.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport { applyDeadzone, attachTimelineInteractions, AXIS_DEADZONE_PX, computeVerticalAxisZoom, DRAG_ACTIVATION_PX } from './timeline-interactions.js';\nimport { timelineStyles } from './timeline-styles.js';\nimport './timeline-track-tabs.js';\n\nimport '@clinth/vcanvas';\n\n// Minimal type for the x-vcanvas element we need\ntype VCanvasElement = HTMLElement & {\n logicalWidth: number;\n logicalHeight: number;\n logicalScrollLeft: number;\n logicalScrollTop: number;\n viewportWidth: number;\n viewportHeight: number;\n drawCallback: ((ctx: CanvasRenderingContext2D, region: Rect, signal: AbortSignal) => void) | null;\n invalidateAll: () => void;\n invalidateRegion: (x: number, y: number, w: number, h: number) => void;\n convertFromViewportToCanvas: (x: number, y: number) => { x: number; y: number };\n convertFromCanvasToViewport: (x: number, y: number) => { x: number; y: number };\n};\n\n/** Reasonable maximum ppu to prevent infinite zoom-in */\nconst MAX_PPU = 10_000;\n\n/** Minimum rendered track height (~1.5em) at max zoom-out */\nconst MIN_TRACK_RENDER_HEIGHT = 24;\n\n@customElement(`ixfx-timeline`)\nexport class IxfxTimelineElement extends LitElement {\n static override styles = [timelineStyles];\n\n // ── Public properties ───────────────────────────────────────────────\n @property({ type: Array }) tracks: TimelineTrack[] = [];\n @property({ type: Array }) events: TimelineEvent[] = [];\n @property({ type: Number }) min = 0;\n @property({ type: Number }) max = 1000;\n @property({ type: String, attribute: `track-height` }) trackHeight: TrackHeightMode = `fixed`;\n @property({ type: String, attribute: `user-scale` }) userScale: UserScale = `normal`;\n @property({ type: Boolean }) reorderable = false;\n @property({ type: Boolean, attribute: `meta` }) metaEnabled = false;\n @property({ type: Boolean, attribute: `alt-major-bg` }) altMajorBg = false;\n @property({ type: Number, attribute: `default-track-height` }) defaultTrackHeight = 48;\n @property({ type: String, attribute: `grid-mode` }) gridMode: GridMode = `adaptive`;\n @property({ type: Number, attribute: `minor-step` }) minorStep = 10;\n @property({ type: Object }) onDragging: DragSnappingFn | null = null;\n\n /** Set by TimelineController — formats logical value to label string */\n formatLabel: (v: number) => string = String;\n\n // ── Internal state ──────────────────────────────────────────────────\n /**\n * _ppu (pixels per unit) is the PRIMARY horizontal zoom state.\n * _viewRange.min is the PRIMARY horizontal pan state.\n * _viewRange.max is DERIVED: min + viewportWidth / _ppu.\n */\n @state() _ppu = 1;\n @state() _viewRange: ViewRange = { min: 0, max: 4000 };\n\n /**\n * Vertical zoom/pan state. Analogous to horizontal:\n * - _vViewRange is in \"base height units\" (pre-scale track pixels).\n * - _vScale = viewportHeight / (b - a). When range spans all base height, all tracks fit.\n * - _vScrollOffset = _vViewRange.min * _vScale — the pixel offset applied to content.\n */\n @state() _vViewRange: ViewRange = { min: 0, max: 1 };\n @state() _vScale = 1.0;\n\n @state() _selectedIds: Set<string> = new Set();\n @state() _tickledId: string | undefined = undefined;\n @state() _drag: DragTarget = { kind: `none` };\n @state() _metaHeight = 120;\n @state() _trackHeightOverrides: Map<string, number> = new Map();\n @state() _trackLayout: TrackLayout[] = [];\n @state() _internalEvents: TimelineEvent[] = [];\n @state() _internalTracks: TimelineTrack[] = [];\n\n // ── Private refs ────────────────────────────────────────────────────\n #vcanvas: VCanvasElement | null = null;\n #cssVarsCache: CssVars = {};\n #cleanupInteractions: (() => void) | null = null;\n #axisHeight = 24;\n #prevTickledId: string | undefined = undefined;\n #resizeObserver: ResizeObserver | null = null;\n #pendingVerticalZoomToFit = false;\n #hasInitializedZoom = false;\n\n // ── Computed getters (for interactions module) ──────────────────────\n\n /** Pixels per logical unit — the zoom level */\n get pxPerUnit(): number {\n return this._ppu;\n }\n\n /** Height of the axis header band in pixels */\n get axisHeight(): number {\n return this.#axisHeight;\n }\n\n /** Width of the track tab area in pixels (from --timeline-tab-width CSS var) */\n get tabWidth(): number {\n return this.#tabWidth;\n }\n\n // ── Viewport helpers ────────────────────────────────────────────────\n\n #getViewportWidth(): number {\n const raw = this.#vcanvas?.viewportWidth\n ?? (this.#vcanvas as HTMLElement | null)?.clientWidth\n ?? 800;\n return raw;\n }\n\n /** Minimum ppu: fits the full total range into the viewport */\n #minPpu(): number {\n const totalSpan = this.max - this.min;\n if (totalSpan <= 0)\n return 1;\n const vpWidth = this.#getViewportWidth();\n return vpWidth / totalSpan;\n }\n\n // ── Vertical viewport helpers ───────────────────────────────────────\n\n /** Available vertical pixels for track content (excludes axis) */\n #getViewportHeight(): number {\n return this.#vcanvas?.viewportHeight\n ?? (this.#vcanvas as HTMLElement | null)?.clientHeight\n ?? 300;\n }\n\n /**\n * Total base height: sum of all visible track base heights (before vScale).\n * This is the \"total range\" for the vertical slider.\n */\n #getTotalBaseHeight(): number {\n let total = 0;\n for (const track of this._internalTracks) {\n if (track.hidden)\n continue;\n const override = this._trackHeightOverrides.get(track.key);\n total += override ?? track.height ?? this.defaultTrackHeight;\n }\n return total || 1; // avoid zero\n }\n\n /** Smallest base height among visible tracks (before vScale) */\n #getSmallestBaseTrackHeight(): number {\n let smallest = Infinity;\n for (const track of this._internalTracks) {\n if (track.hidden)\n continue;\n const override = this._trackHeightOverrides.get(track.key);\n const h = override ?? track.height ?? this.defaultTrackHeight;\n if (h < smallest)\n smallest = h;\n }\n return smallest === Infinity ? this.defaultTrackHeight : smallest;\n }\n\n /** Largest base height among visible tracks (before vScale) */\n #getLargestBaseTrackHeight(): number {\n let largest = 0;\n for (const track of this._internalTracks) {\n if (track.hidden)\n continue;\n const override = this._trackHeightOverrides.get(track.key);\n const h = override ?? track.height ?? this.defaultTrackHeight;\n if (h > largest)\n largest = h;\n }\n return largest || this.defaultTrackHeight;\n }\n\n /**\n * Derive vScale and vViewRange from a given vertical view range.\n * viewportHeight / span = scale factor applied to base track heights.\n * The axis header takes up fixed space, so available height for tracks is reduced.\n */\n #deriveVerticalZoom(vMin: number, vMax: number): void {\n const totalBase = this.#getTotalBaseHeight();\n const vpHeight = this.#getViewportHeight() - this.#axisHeight;\n\n // Clamp range to [0, totalBase]\n let a = vMin;\n let b = vMax;\n\n // Min span (max zoom-in): largest visible track fills the viewport\n const largestBase = this.#getLargestBaseTrackHeight();\n const minSpan = Math.max(largestBase, totalBase * 0.05);\n\n // Max span (max zoom-out): smallest visible track must not shrink below MIN_TRACK_RENDER_HEIGHT\n // Since renderedHeight = baseHeight * vScale and vScale = vpHeight / span,\n // we need: smallestBase * (vpHeight / span) >= MIN_TRACK_RENDER_HEIGHT\n // → span <= vpHeight * smallestBase / MIN_TRACK_RENDER_HEIGHT\n const smallestBase = this.#getSmallestBaseTrackHeight();\n const maxSpanFromMinHeight = vpHeight > 0\n ? vpHeight * smallestBase / MIN_TRACK_RENDER_HEIGHT\n : totalBase;\n const maxSpan = Math.min(totalBase, maxSpanFromMinHeight);\n\n let span = b - a;\n if (span < minSpan) {\n const center = (a + b) / 2;\n a = center - minSpan / 2;\n b = center + minSpan / 2;\n span = minSpan;\n }\n if (span > maxSpan) {\n const center = (a + b) / 2;\n a = center - maxSpan / 2;\n b = center + maxSpan / 2;\n span = maxSpan;\n }\n\n if (b > totalBase) {\n b = totalBase;\n a = Math.max(0, b - span);\n }\n if (a < 0) {\n a = 0;\n b = Math.min(totalBase, a + span);\n }\n\n this._vViewRange = { min: a, max: b };\n this._vScale = vpHeight > 0 ? vpHeight / (b - a) : 1;\n }\n\n /** Reset vertical zoom to fit all tracks */\n #initVerticalZoomToFit(): void {\n const totalBase = this.#getTotalBaseHeight();\n this.#deriveVerticalZoom(0, totalBase);\n }\n\n /** Pixel offset to scroll the content area */\n get #vScrollOffset(): number {\n return this._vViewRange.min * this._vScale;\n }\n\n /**\n * Derive viewRange from ppu + viewMin, clamped to [min, max].\n * Returns a new ViewRange.\n */\n #deriveViewRange(viewMin: number, ppu: number): ViewRange {\n const vpWidth = this.#getViewportWidth();\n const viewSpan = ppu > 0 ? vpWidth / ppu : (this.max - this.min);\n let newMin = viewMin;\n let newMax = newMin + viewSpan;\n\n // Clamp: don't let max exceed total max\n if (newMax > this.max) {\n newMax = this.max;\n newMin = Math.max(this.min, newMax - viewSpan);\n }\n // Clamp: don't let min go below total min\n if (newMin < this.min) {\n newMin = this.min;\n newMax = Math.min(this.max, newMin + viewSpan);\n }\n\n return { min: newMin, max: newMax };\n }\n\n // ── Public methods: zoom & pan ──────────────────────────────────────\n\n /** Set the zoom level (pixels per logical unit) */\n setPpu(ppu: number): void {\n this._ppu = Math.max(this.#minPpu(), Math.min(ppu, MAX_PPU));\n this._viewRange = this.#deriveViewRange(this._viewRange.min, this._ppu);\n }\n\n /**\n * Set the view range. ppu is back-computed from the span.\n * Use for programmatic view changes (e.g. TimelineController).\n */\n setViewRange(range: ViewRange): void {\n const vpWidth = this.#getViewportWidth();\n const span = range.max - range.min;\n if (span > 0) {\n this._ppu = Math.max(this.#minPpu(), Math.min(vpWidth / span, MAX_PPU));\n }\n this._viewRange = this.#deriveViewRange(range.min, this._ppu);\n }\n\n /**\n * Set zoom + pan atomically. Used by axis-zoom interaction.\n * viewMin is the left edge in timeline units; ppu is the new zoom level.\n */\n setZoomPan(viewMin: number, ppu: number): void {\n this._ppu = Math.max(this.#minPpu(), Math.min(ppu, MAX_PPU));\n this._viewRange = this.#deriveViewRange(viewMin, this._ppu);\n }\n\n /**\n * Set vertical zoom + pan atomically. Used by v-axis-zoom interaction.\n * vMin/vMax define the vertical view range in base-height units.\n */\n setVerticalZoomPan(vMin: number, vMax: number): void {\n this.#deriveVerticalZoom(vMin, vMax);\n }\n\n selectAll(): void {\n this._selectedIds = new Set(this._internalEvents.map(e => e.id));\n this.#fireSelectionChange();\n this.#invalidateCanvas();\n }\n\n deselectAll(): void {\n this._selectedIds = new Set();\n this.#fireSelectionChange();\n this.#invalidateCanvas();\n }\n\n getTrackEvents(trackKey: string): TimelineEvent[] {\n return this._internalEvents.filter(e => e.trackKey === trackKey);\n }\n\n get selectedIds(): readonly string[] {\n return [...this._selectedIds];\n }\n\n set selectedIds(ids: readonly string[]) {\n this._selectedIds = new Set(ids);\n this.#fireSelectionChange();\n this.#invalidateCanvas();\n }\n\n selectTrack(key: string, shiftKey: boolean = false): void {\n this.dispatchEvent(\n new CustomEvent<TimelineTrackSelectDetail>(`track-select`, {\n detail: { trackKey: key, shiftKey },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n /** Resets all track height overrides to the default height and resets vertical zoom. */\n resetTrackHeights(): void {\n this._trackHeightOverrides = new Map();\n this.#pendingVerticalZoomToFit = true;\n // willUpdate will see the flag and reset vertical zoom during the same update cycle\n }\n\n /** Default no-op snapping - no modification of drag values */\n static defaultDragSnapping: DragSnappingFn = () => ({});\n\n /** Grid-snapping - snaps start/end to nearest minor gridline */\n static gridSnapping: DragSnappingFn = (detail) => {\n const { gridResolution, currentStart, currentEnd } = detail;\n const { minorStep } = gridResolution;\n return {\n start: Math.round(currentStart / minorStep) * minorStep,\n end: Math.round(currentEnd / minorStep) * minorStep,\n };\n };\n\n /**\n * Called by timeline-interactions during drag.\n * Applies onDragging callback if set, then fires 'dragging' event.\n * Returns constrained values.\n */\n processDrag(\n eventId: string,\n rawLogicalX: number,\n rawY: number,\n currentStart: number,\n currentEnd: number,\n currentTrackKey: string,\n originTrackKey: string,\n gridResolution: GridResolution,\n ): { start: number; end: number; trackKey: string } {\n let constrainedStart = currentStart;\n let constrainedEnd = currentEnd;\n let constrainedTrackKey = currentTrackKey;\n\n if (this.onDragging) {\n const result = this.onDragging({\n eventId,\n rawLogicalX,\n rawY,\n currentStart,\n currentEnd,\n currentTrackKey,\n gridResolution,\n originTrackKey,\n });\n constrainedStart = result.start ?? currentStart;\n constrainedEnd = result.end ?? currentEnd;\n constrainedTrackKey = result.trackKey ?? currentTrackKey;\n }\n\n this.dispatchEvent(\n new CustomEvent(`dragging`, {\n detail: {\n eventId,\n start: constrainedStart,\n end: constrainedEnd,\n trackKey: constrainedTrackKey,\n rawLogicalX,\n rawY,\n originTrackKey,\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n return {\n start: constrainedStart,\n end: constrainedEnd,\n trackKey: constrainedTrackKey,\n };\n }\n\n /** Returns the vcanvas element (for timeline-interactions) */\n getVCanvas(): VCanvasElement | null {\n return this.#vcanvas;\n }\n\n /** Returns the currently hovered (tickled) event, or null if none. */\n get tickledEvent(): TimelineEvent | null {\n if (this._tickledId === undefined)\n return null;\n return this._internalEvents.find(e => e.id === this._tickledId) ?? null;\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────────\n\n protected override firstUpdated(): void {\n this.#vcanvas = this.shadowRoot?.querySelector(`x-vcanvas`) as VCanvasElement | null;\n\n if (this.#vcanvas) {\n this.#vcanvas.drawCallback = this.#drawCallback;\n this.#vcanvas.addEventListener(`scroll-virtual`, this.#onScrollVirtual as EventListener);\n this.#cleanupInteractions = attachTimelineInteractions(this, this.#vcanvas);\n\n // Watch for viewport resizes — adjust viewRange.max, keep ppu stable.\n // The observer fires immediately with the initial size, which handles\n // the initial zoom-to-fit without setting state in firstUpdated.\n this.#resizeObserver = new ResizeObserver(this.#onResize);\n this.#resizeObserver.observe(this.#vcanvas);\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#cleanupInteractions?.();\n this.#cleanupInteractions = null;\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = null;\n }\n\n /**\n * willUpdate runs before render — safe to set @state() properties here\n * without triggering an additional update cycle. All state derivation\n * (internal copies, track layout, vertical zoom) happens here.\n */\n protected override willUpdate(changedProps: PropertyValues): void {\n // Sync external tracks/events into internal managed copies on shallow change\n let needsLayoutRecompute = false;\n let needsVerticalZoomReset = false;\n let needsHorizontalZoomToFit = false;\n\n if (changedProps.has(`tracks`)) {\n this._internalTracks = [...this.tracks];\n needsLayoutRecompute = true;\n needsVerticalZoomReset = true;\n }\n if (changedProps.has(`events`)) {\n this._internalEvents = [...this.events];\n }\n if (changedProps.has(`trackHeight`)) {\n // Mode changed — clear per-track overrides when leaving 'free' mode\n if (this.trackHeight !== `free` && this._trackHeightOverrides.size > 0) {\n this._trackHeightOverrides = new Map();\n }\n needsLayoutRecompute = true;\n needsVerticalZoomReset = true;\n }\n if (changedProps.has(`_vScale`) || changedProps.has(`defaultTrackHeight`) || changedProps.has(`_trackHeightOverrides`)) {\n needsLayoutRecompute = true;\n }\n if (changedProps.has(`min`) || changedProps.has(`max`)) {\n needsHorizontalZoomToFit = true;\n }\n\n // Consume pending zoom-to-fit flag (from resetTrackHeights)\n if (this.#pendingVerticalZoomToFit) {\n this.#pendingVerticalZoomToFit = false;\n needsVerticalZoomReset = true;\n }\n\n // Horizontal zoom-to-fit when total range changes\n if (needsHorizontalZoomToFit) {\n this.#initZoomToFit();\n }\n // Vertical zoom reset must happen BEFORE layout recompute (layout uses _vScale)\n if (needsVerticalZoomReset) {\n this.#initVerticalZoomToFit();\n }\n if (needsLayoutRecompute) {\n this.#recomputeTrackLayout();\n }\n }\n\n /**\n * updated runs after render — for DOM side-effects only (syncing vcanvas,\n * scrolling, invalidating canvas, dispatching events).\n */\n protected override updated(changedProps: PropertyValues): void {\n if (changedProps.has(`_ppu`) || changedProps.has(`_viewRange`) || changedProps.has(`min`) || changedProps.has(`max`)) {\n this.#syncVCanvas();\n this.#invalidateCanvas();\n }\n if (changedProps.has(`events`) || changedProps.has(`userScale`) || changedProps.has(`altMajorBg`) || changedProps.has(`gridMode`) || changedProps.has(`minorStep`)) {\n this.#invalidateCanvas();\n }\n if (changedProps.has(`_trackLayout`)) {\n this.#syncVCanvasVertical();\n this.#positionTabs();\n this.#invalidateCanvas();\n }\n\n // Sync vertical zoom/pan to vcanvas logicalScrollTop + logicalHeight\n if (changedProps.has(`_vViewRange`) || changedProps.has(`_vScale`)) {\n this.#syncVerticalScroll();\n this.#invalidateCanvas();\n }\n\n if (this._tickledId !== this.#prevTickledId) {\n this.#prevTickledId = this._tickledId;\n this.dispatchEvent(\n new CustomEvent<TimelineTickleChangeDetail>(`tickle-change`, {\n detail: { event: this.tickledEvent },\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n protected override render() {\n const totalBaseHeight = this.#getTotalBaseHeight();\n\n return html`\n <div class=\"content-wrapper\">\n <div class=\"content-area\">\n <div class=\"v-axis-zone\"\n @pointerdown=${this.#onVAxisPointerDown}\n @pointermove=${this.#onVAxisPointerMove}\n @pointerup=${this.#onVAxisPointerUp}\n @pointercancel=${this.#onVAxisPointerUp}\n ></div>\n <div class=\"tab-column\">\n ${this._internalTracks\n .filter(t => !t.hidden)\n .map((t) => {\n const layout = this._trackLayout.find(l => l.trackKey === t.key);\n if (!layout)\n return nothing;\n return html`\n <ixfx-timeline-track-tab\n .label=${t.label}\n track-key=\"${t.key}\"\n ?resizable=${this.trackHeight !== `none`}\n ?reorderable=${this.reorderable}\n @click=${(e: MouseEvent) => this.selectTrack(t.key, e.shiftKey)}\n @track-resize-move=${this.#onTrackResizeMove}\n @track-resize=${this.#onTrackResizeEnd}\n ></ixfx-timeline-track-tab>\n `;\n })}\n </div>\n <x-vcanvas></x-vcanvas>\n </div>\n <div class=\"v-zoom\">\n <ixfx-range-multi\n orientation=\"vertical\"\n min=\"0\"\n max=\"${totalBaseHeight}\"\n a=\"${this._vViewRange.min}\"\n b=\"${this._vViewRange.max}\"\n step=\"any\"\n crossover=\"stop\"\n resize-mode=\"bipolar-flex\"\n @input=${this.#onVZoomInput}\n ></ixfx-range-multi>\n </div>\n </div>\n\n <div class=\"h-zoom\">\n <ixfx-range-multi\n min=\"${this.min}\"\n max=\"${this.max}\"\n a=\"${this._viewRange.min}\"\n b=\"${this._viewRange.max}\"\n crossover=\"stop\"\n resize-mode=\"bipolar-flex\"\n @input=${this.#onHZoomInput}\n ></ixfx-range-multi>\n </div>\n\n <div class=\"meta-resizer ${this.metaEnabled ? `visible` : ``}\"></div>\n <div class=\"meta-area ${this.metaEnabled ? `visible` : ``}\" style=\"height:${this._metaHeight}px\"></div>\n `;\n }\n\n // ── VCanvas wiring ──────────────────────────────────────────────────\n\n /** Compute initial ppu to fit 80% of the range, then derive viewRange */\n #initZoomToFit(): void {\n const vpWidth = this.#getViewportWidth();\n const totalSpan = this.max - this.min;\n const zoomFactor = 0.8;\n this._ppu = totalSpan > 0 ? vpWidth / (totalSpan * zoomFactor) : 1;\n this._viewRange = { min: this.min, max: this.min + totalSpan * zoomFactor };\n this.#syncVCanvas();\n // ResizeObserver will re-derive viewRange once vcanvas has its final layout\n }\n\n #syncVCanvas(): void {\n if (!this.#vcanvas)\n return;\n const totalSpan = this.max - this.min;\n // logicalWidth = pure timeline content width in pixels (no tab offset)\n this.#vcanvas.logicalWidth = totalSpan * this._ppu;\n this.#syncVCanvasVertical();\n this.#syncVCanvasScroll();\n }\n\n /** Sync logicalHeight and logicalScrollTop to the vcanvas */\n #syncVCanvasVertical(): void {\n if (!this.#vcanvas)\n return;\n const totalHeight = this._trackLayout.reduce((sum, tl) => sum + tl.height, 0);\n const canvasHeight = Math.max(totalHeight, 40) + this.#axisHeight;\n this.#vcanvas.logicalHeight = canvasHeight;\n this.#vcanvas.logicalScrollTop = this.#vScrollOffset;\n }\n\n #syncVCanvasScroll(): void {\n if (!this.#vcanvas)\n return;\n // Scroll position in logical pixels: pure timeline scroll offset (no tab area)\n this.#vcanvas.logicalScrollLeft = (this._viewRange.min - this.min) * this._ppu;\n }\n\n #invalidateCanvas(): void {\n this.#vcanvas?.invalidateAll();\n }\n\n /** Called by ResizeObserver when vcanvas/content-area changes size */\n #onResize = (_entries: ResizeObserverEntry[]): void => {\n if (!this.#hasInitializedZoom) {\n // First resize after mount — compute initial zoom-to-fit\n this.#hasInitializedZoom = true;\n this.#initZoomToFit();\n this.#initVerticalZoomToFit();\n this.#recomputeTrackLayout();\n } else {\n // Subsequent resizes — keep ppu/vViewRange, re-derive from new viewport size\n this._viewRange = this.#deriveViewRange(this._viewRange.min, this._ppu);\n this.#deriveVerticalZoom(this._vViewRange.min, this._vViewRange.max);\n }\n this.#syncVCanvas();\n\n this.#invalidateCanvas();\n };\n\n #drawCallback = (ctx: CanvasRenderingContext2D, region: Rect, _signal: AbortSignal): void => {\n const ppu = this._ppu;\n\n // Convert pixel region back to timeline logical units for culling.\n // region.x is in vcanvas logical pixels (pure timeline content, no tab offset).\n const logicalRegion: Rect = {\n x: region.x / ppu + this.min,\n y: region.y,\n width: region.width / ppu,\n height: region.height,\n };\n\n // Resolve CSS vars once per frame\n this.#cssVarsCache = resolveCssVars(this);\n\n const grid = resolveGrid(\n this._viewRange,\n this.userScale,\n this.#getViewportWidth(),\n this.gridMode,\n this.minorStep,\n ppu,\n );\n\n // 1. Track backgrounds\n const trackInfos = this._internalTracks.map(t => ({ key: t.key, color: t.color }));\n drawTrackBackgrounds(ctx, logicalRegion, this._trackLayout, trackInfos, this.#cssVarsCache, ppu);\n\n // 2. Grid\n drawMajorMinorGrid(ctx, logicalRegion, grid, this.altMajorBg, this.#cssVarsCache, ppu);\n\n // 3. Event rects\n drawEventRects(\n ctx,\n logicalRegion,\n this._internalEvents,\n this._trackLayout,\n this._selectedIds,\n this._tickledId,\n this._drag,\n this.#cssVarsCache,\n ppu,\n );\n\n // 4. Marquee\n drawMarquee(ctx, this._drag, ppu);\n\n // 5. Axis (drawn last so it overlays grid lines in the axis band)\n const scrollTop = this.#vcanvas?.logicalScrollTop ?? 0;\n drawAxis(ctx, logicalRegion, grid, this.#axisHeight, this.formatLabel, this.#cssVarsCache, ppu, scrollTop);\n };\n\n // ── Scroll / axis labels ────────────────────────────────────────────\n\n #onScrollVirtual = (e: Event): void => {\n const detail = (e as CustomEvent<{\n logicalScrollLeft: number;\n logicalScrollTop: number;\n viewportStartLogical: number;\n viewportEndLogical: number;\n userGesture?: boolean;\n }>).detail;\n\n // Only react to user-initiated scrolls to avoid feedback loops\n if (detail.userGesture === false)\n return;\n\n const ppu = this._ppu;\n if (ppu <= 0)\n return;\n\n // Horizontal: convert pixel scroll position to timeline units (no tab offset)\n const viewMin = detail.logicalScrollLeft / ppu + this.min;\n this._viewRange = this.#deriveViewRange(viewMin, ppu);\n\n // Vertical: reverse-derive _vViewRange from the new logicalScrollTop\n // logicalScrollTop = _vViewRange.min * _vScale, so _vViewRange.min = logicalScrollTop / _vScale\n if (this._vScale > 0) {\n const span = this._vViewRange.max - this._vViewRange.min;\n const newVMin = detail.logicalScrollTop / this._vScale;\n this.#deriveVerticalZoom(newVMin, newVMin + span);\n }\n };\n\n // ── Private helpers ─────────────────────────────────────────────────\n\n #recomputeTrackLayout(): void {\n const layout: TrackLayout[] = [];\n let y = this.#axisHeight;\n for (const track of this._internalTracks) {\n if (track.hidden)\n continue;\n const override = this._trackHeightOverrides.get(track.key);\n const baseHeight = override ?? track.height ?? this.defaultTrackHeight;\n const height = Math.round(baseHeight * this._vScale);\n layout.push({ trackKey: track.key, y, height });\n y += height;\n }\n this._trackLayout = layout;\n }\n\n /**\n * Range slider handler.\n * Slider controls zoom (via span) + pan (via position).\n * Derive ppu from the span the slider produces.\n */\n #onHZoomInput = (e: Event): void => {\n const detail = (e as CustomEvent<{ a: number; b: number }>).detail;\n const span = detail.b - detail.a;\n const vpWidth = this.#getViewportWidth();\n if (span > 0) {\n this._ppu = Math.max(this.#minPpu(), Math.min(vpWidth / span, MAX_PPU));\n }\n this._viewRange = this.#deriveViewRange(detail.a, this._ppu);\n this.#syncVCanvas();\n };\n\n /**\n * Vertical range slider handler.\n * Slider controls vertical zoom (via span) + vertical pan (via position).\n * Derive _vScale from the span the slider produces.\n */\n #onVZoomInput = (e: Event): void => {\n const detail = (e as CustomEvent<{ a: number; b: number }>).detail;\n this.#deriveVerticalZoom(detail.a, detail.b);\n };\n\n // ── Vertical axis drag-zoom handlers ──────────────────────────────────\n\n #onVAxisPointerDown = (e: PointerEvent): void => {\n const zone = e.currentTarget as HTMLElement;\n zone.setPointerCapture(e.pointerId);\n e.stopPropagation(); // prevent track-tab click/select\n e.preventDefault();\n\n // Compute anchor in base-height units from the click Y position\n // The zone covers the track area (below the axis header padding)\n const rect = zone.getBoundingClientRect();\n const clickFrac = rect.height > 0\n ? (e.clientY - rect.top) / rect.height\n : 0.5;\n const span = this._vViewRange.max - this._vViewRange.min;\n const anchorBaseY = this._vViewRange.min + clickFrac * span;\n\n this._drag = {\n kind: `v-axis-zoom`,\n anchorBaseY,\n startScreenX: e.screenX,\n startScreenY: e.screenY,\n startVMin: this._vViewRange.min,\n startVMax: this._vViewRange.max,\n startVScale: this._vScale,\n };\n };\n\n #onVAxisPointerMove = (e: PointerEvent): void => {\n if (this._drag.kind !== `v-axis-zoom`)\n return;\n const drag = this._drag;\n\n const rawDx = e.screenX - drag.startScreenX;\n const rawDy = e.screenY - drag.startScreenY;\n\n // Activation threshold — don't start until enough total movement\n if (Math.abs(rawDx) + Math.abs(rawDy) < DRAG_ACTIVATION_PX)\n return;\n\n // Per-axis deadzone — ignore small off-axis movement\n const dx = applyDeadzone(rawDx, AXIS_DEADZONE_PX);\n const dy = applyDeadzone(rawDy, AXIS_DEADZONE_PX);\n\n const result = computeVerticalAxisZoom(\n drag.startVMin,\n drag.startVMax,\n drag.startVScale,\n drag.anchorBaseY,\n dx,\n dy,\n );\n this.setVerticalZoomPan(result.vMin, result.vMax);\n };\n\n #onVAxisPointerUp = (e: PointerEvent): void => {\n if (this._drag.kind !== `v-axis-zoom`)\n return;\n (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);\n this._drag = { kind: `none` };\n };\n\n /** Sync vertical scroll position to vcanvas logicalScrollTop */\n #syncVerticalScroll(): void {\n this.#syncVCanvasVertical();\n this.#positionTabs();\n }\n\n /**\n * Position track tabs in the .tab-column via absolute CSS positioning.\n * Applies a vertical translateY to offset for the current vertical scroll.\n */\n #positionTabs(): void {\n const tabColumn = this.shadowRoot?.querySelector<HTMLElement>(`.tab-column`);\n if (!tabColumn)\n return;\n\n const scrollTop = this.#vcanvas?.logicalScrollTop ?? 0;\n tabColumn.style.transform = `translateY(${-scrollTop}px)`;\n\n for (const layout of this._trackLayout) {\n const tab = tabColumn.querySelector<HTMLElement>(\n `ixfx-timeline-track-tab[track-key=\"${layout.trackKey}\"]`,\n );\n if (!tab)\n continue;\n tab.style.top = `${layout.y}px`;\n tab.style.height = `${layout.height}px`;\n }\n }\n\n /** Read the tab width from the CSS custom property */\n get #tabWidth(): number {\n const raw = getComputedStyle(this).getPropertyValue(`--timeline-tab-width`).trim();\n return Number.parseInt(raw, 10) || 120;\n }\n\n // ── Track resize handlers ────────────────────────────────────────────\n\n /** Live preview during drag — update heights but don't fire public event */\n #onTrackResizeMove = (e: Event): void => {\n const { trackKey, height } = (e as CustomEvent<TimelineTrackResizeDetail>).detail;\n this.#applyTrackResize(trackKey, height);\n };\n\n /** Final resize on pointerup — apply height and fire public event */\n #onTrackResizeEnd = (e: Event): void => {\n const { trackKey, height } = (e as CustomEvent<TimelineTrackResizeDetail>).detail;\n this.#applyTrackResize(trackKey, height);\n\n this.dispatchEvent(\n new CustomEvent<TimelineTrackResizeDetail>(`track-resize`, {\n detail: { trackKey, height },\n bubbles: true,\n composed: true,\n }),\n );\n };\n\n /**\n * Apply track height based on the current `trackHeight` mode.\n * - `fixed`: sets `defaultTrackHeight` (all tracks change uniformly)\n * - `free`: sets per-track override in `_trackHeightOverrides`\n * - `none`: should never be called (resize is disabled)\n */\n #applyTrackResize(trackKey: string, height: number): void {\n if (this.trackHeight === `fixed`) {\n this.defaultTrackHeight = height;\n } else if (this.trackHeight === `free`) {\n const next = new Map(this._trackHeightOverrides);\n next.set(trackKey, height);\n this._trackHeightOverrides = next;\n }\n // `updated()` will trigger #recomputeTrackLayout via defaultTrackHeight or _trackHeightOverrides change\n }\n\n #fireSelectionChange(): void {\n this.dispatchEvent(\n new CustomEvent<TimelineSelectionChangeDetail>(`selection-change`, {\n detail: { selectedIds: [...this._selectedIds] },\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n\n// ── Global tag name registration ──────────────────────────────────────\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-timeline': IxfxTimelineElement;\n }\n}\n\n// ── Module-scope interface merge (typed addEventListener) ─────────────\nexport interface IxfxTimelineElement {\n addEventListener: (<K extends keyof TimelineElementEventMap>(\n type: K,\n listener: (this: IxfxTimelineElement, event: TimelineElementEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n}\n","import type { ViewRange, UserScale, GridResolution, TimelineEvent, TimelineTrackSelectDetail } from './types.js';\n\n/**\n * Configuration for the TimelineController external adapter.\n */\nexport type TimelineControllerConfig = {\n readonly logicalMin: number;\n readonly logicalMax: number;\n readonly labelFormatter?: (value: number) => string;\n readonly gridResolver?: (viewRange: ViewRange, userScale: UserScale) => GridResolution;\n};\n\n/**\n * Minimal interface the controller expects from the timeline element.\n * Avoids coupling to the concrete element class.\n */\ntype TimelineHost = {\n formatLabel: (v: number) => string;\n setViewRange(r: ViewRange): void;\n min: number;\n max: number;\n userScale: string;\n getTrackEvents(trackKey: string): TimelineEvent[];\n get selectedIds(): readonly string[];\n set selectedIds(ids: readonly string[]);\n addEventListener(type: string, listener: EventListener): void;\n removeEventListener(type: string, listener: EventListener): void;\n};\n\n/**\n * External adapter class that can configure a timeline element\n * with musical time, custom label formatting, etc.\n * \n * The controller has no import of any timeline element type.\n * Users create a controller, pass it the component instance,\n * and the controller calls the component's public API.\n */\nexport class TimelineController {\n #host: TimelineHost;\n #config: TimelineControllerConfig;\n #bpm = 120;\n #timeSignatureNumerator = 4;\n #timeSignatureDenominator = 4;\n\n constructor(timeline: TimelineHost, config: TimelineControllerConfig) {\n this.#host = timeline;\n this.#config = config;\n\n // Apply initial configuration\n timeline.min = config.logicalMin;\n timeline.max = config.logicalMax;\n\n if (config.labelFormatter) {\n timeline.formatLabel = config.labelFormatter;\n }\n\n timeline.addEventListener(`track-select`, this.#onTrackSelect as EventListener);\n }\n\n /** Update the tempo (BPM) for musical time calculations. */\n setTempo(bpm: number): void {\n this.#bpm = bpm;\n this.#updateFormatLabel();\n }\n\n /** Update the time signature for musical time calculations. */\n setTimeSignature(numerator: number, denominator: number): void {\n this.#timeSignatureNumerator = numerator;\n this.#timeSignatureDenominator = denominator;\n this.#updateFormatLabel();\n }\n\n /** Set the visible view range on the timeline. */\n setViewRange(range: ViewRange): void {\n this.#host.setViewRange(range);\n }\n\n /** Clean up any resources held by the controller. */\n dispose(): void {\n this.#host.removeEventListener(`track-select`, this.#onTrackSelect as EventListener);\n }\n\n #onTrackSelect = (e: Event): void => {\n const detail = (e as CustomEvent<TimelineTrackSelectDetail>).detail;\n const trackEventIds = this.#host.getTrackEvents(detail.trackKey).map(e => e.id);\n\n if (detail.shiftKey) {\n const current = new Set(this.#host.selectedIds);\n for (const id of trackEventIds) {\n if (current.has(id)) {\n current.delete(id);\n } else {\n current.add(id);\n }\n }\n this.#host.selectedIds = [...current];\n } else {\n this.#host.selectedIds = trackEventIds;\n }\n };\n\n #updateFormatLabel(): void {\n if (this.#config.labelFormatter) {\n // User provided a custom formatter — don't override\n return;\n }\n\n const bpm = this.#bpm;\n const num = this.#timeSignatureNumerator;\n\n // Musical time formatter: convert logical units to bars:beats\n this.#host.formatLabel = (v: number) => {\n const beatsPerBar = num;\n const totalBeats = v; // Assume 1 logical unit = 1 beat\n const bar = Math.floor(totalBeats / beatsPerBar) + 1;\n const beat = Math.floor(totalBeats % beatsPerBar) + 1;\n return `${bar}.${beat}`;\n };\n }\n}\n"],"mappings":"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"}
@@ -1,190 +0,0 @@
1
- //#region src/common/tree-data-model.ts
2
- function findNode(root, key) {
3
- if (root.item.key === key) return root;
4
- if (!root.children) return void 0;
5
- for (const child of root.children) {
6
- const found = findNode(child, key);
7
- if (found) return found;
8
- }
9
- }
10
- /** Return a new root with the node at `key` replaced by `replacer(node)`. */
11
- function mapNode(root, key, replacer) {
12
- if (root.item.key === key) return replacer(root);
13
- if (!root.children) return root;
14
- return {
15
- item: root.item,
16
- children: root.children.map((c) => mapNode(c, key, replacer))
17
- };
18
- }
19
- /** Return a new root with `newChild` appended to the children of the node at `parentKey`. */
20
- function addChildTo(root, parentKey, newChild) {
21
- return mapNode(root, parentKey, (parent) => ({
22
- item: parent.item,
23
- children: [...parent.children ?? [], newChild]
24
- }));
25
- }
26
- /** Return a new root with the node at `key` removed (along with all its descendants). */
27
- function removeNodeFrom(root, key) {
28
- if (!root.children) return root;
29
- return {
30
- item: root.item,
31
- children: root.children.filter((c) => c.item.key !== key).map((c) => removeNodeFrom(c, key))
32
- };
33
- }
34
- /** Return a new root with the node at `key` moved to become a child of `newParentKey`. */
35
- function moveNodeTo(root, key, newParentKey) {
36
- const target = findNode(root, key);
37
- if (!target) return root;
38
- return addChildTo(removeNodeFrom(root, key), newParentKey, target);
39
- }
40
- /**
41
- * Manages tree data for tree-like components.
42
- *
43
- * Handles two concerns:
44
- * 1. **Lazy loading** — caches children fetched via `loadChildren` and can invalidate
45
- * specific branches so they are re-fetched on next expand.
46
- * 2. **Structural mutations** — convenience methods to add, remove, update, or move
47
- * nodes. Each mutation produces a new immutable root and notifies connected components.
48
- *
49
- * Assign an instance to a component's `model` property instead of setting `root`/`loadChildren`
50
- * directly. The same model instance can be shared across multiple components.
51
- *
52
- * @example
53
- * ```typescript
54
- * const model = new TreeDataModel({
55
- * root: { item: { key: 'root', label: 'Root' }, children: [] },
56
- * loadChildren: async (node, depth, signal) => fetchChildren(node.item.key),
57
- * });
58
- * myTreeList.model = model;
59
- * myMillerList.model = model;
60
- *
61
- * // Later — invalidate a branch to force a re-fetch on next expand:
62
- * model.invalidate('some-key');
63
- * ```
64
- */
65
- var TreeDataModel = class {
66
- #root;
67
- #loadChildren;
68
- #cache = /* @__PURE__ */ new Map();
69
- #subscribers = /* @__PURE__ */ new Set();
70
- constructor(options) {
71
- this.#root = options.root;
72
- this.#loadChildren = options.loadChildren;
73
- }
74
- /** The current immutable root snapshot. */
75
- get root() {
76
- return this.#root;
77
- }
78
- /**
79
- * Replace the entire root with a new immutable snapshot.
80
- * All cached children are cleared. Connected components re-render.
81
- */
82
- setRoot(root) {
83
- this.#root = root;
84
- this.#cache.clear();
85
- this.#notify({
86
- type: "root",
87
- root
88
- });
89
- }
90
- /**
91
- * Returns children for the given node.
92
- * If children are cached, they are returned immediately (as a resolved Promise).
93
- * Otherwise the `loadChildren` callback is invoked and the result is cached.
94
- * If no `loadChildren` is set and no cache entry exists, returns `[]`.
95
- *
96
- * Called by components internally — not typically called by application code.
97
- */
98
- async getChildren(node, depth, signal) {
99
- const key = node.item.key;
100
- if (this.#cache.has(key)) return this.#cache.get(key);
101
- if (!this.#loadChildren) return [];
102
- const result = await this.#loadChildren(node, depth, signal);
103
- if (!signal.aborted) this.#cache.set(key, result);
104
- return result;
105
- }
106
- /**
107
- * Clears the cached children for the node with the given key.
108
- * Connected components will collapse that node (if currently expanded), so that
109
- * the next expand triggers a fresh `loadChildren` call.
110
- */
111
- invalidate(key) {
112
- this.#cache.delete(key);
113
- this.#notify({
114
- type: "invalidate",
115
- key
116
- });
117
- }
118
- /**
119
- * Adds `node` as a child of the node identified by `parentKey`.
120
- * Returns the new root (connected components are notified automatically).
121
- */
122
- addNode(parentKey, node) {
123
- this.#root = addChildTo(this.#root, parentKey, node);
124
- this.#notify({
125
- type: "root",
126
- root: this.#root
127
- });
128
- return this.#root;
129
- }
130
- /**
131
- * Removes the node with the given key and all its descendants.
132
- * Returns the new root (connected components are notified automatically).
133
- */
134
- removeNode(key) {
135
- this.#cache.delete(key);
136
- this.#root = removeNodeFrom(this.#root, key);
137
- this.#notify({
138
- type: "root",
139
- root: this.#root
140
- });
141
- return this.#root;
142
- }
143
- /**
144
- * Applies a partial update to the `item` of the node with the given key.
145
- * Returns the new root (connected components are notified automatically).
146
- */
147
- updateItem(key, patch) {
148
- this.#root = mapNode(this.#root, key, (n) => ({
149
- item: {
150
- ...n.item,
151
- ...patch
152
- },
153
- children: n.children
154
- }));
155
- this.#notify({
156
- type: "root",
157
- root: this.#root
158
- });
159
- return this.#root;
160
- }
161
- /**
162
- * Moves the node with the given key to become a child of `newParentKey`.
163
- * Returns the new root (connected components are notified automatically).
164
- */
165
- moveNode(key, newParentKey) {
166
- this.#root = moveNodeTo(this.#root, key, newParentKey);
167
- this.#notify({
168
- type: "root",
169
- root: this.#root
170
- });
171
- return this.#root;
172
- }
173
- /**
174
- * Subscribe to model events. Returns an unsubscribe function.
175
- * Components call this when the `model` property is set, and unsubscribe when it is unset.
176
- *
177
- * @internal
178
- */
179
- _subscribe(callback) {
180
- this.#subscribers.add(callback);
181
- return () => this.#subscribers.delete(callback);
182
- }
183
- #notify(event) {
184
- for (const sub of this.#subscribers) sub(event);
185
- }
186
- };
187
- //#endregion
188
- export { TreeDataModel as t };
189
-
190
- //# sourceMappingURL=tree-data-model-DGvRVOFY.js.map