timeline-canvas 1.3.0 → 1.4.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 (38) hide show
  1. package/README.md +21 -0
  2. package/README_CN.md +21 -0
  3. package/dist/ContextMenuPlugin-1N7od2I9.mjs +1 -0
  4. package/dist/{ContextMenuPlugin-HeXhKHl7.d.mts → ContextMenuPlugin-Ce29-J1v.d.mts} +1 -1
  5. package/dist/{DarkThemePlugin-Dy3wNEsj.d.mts → DarkThemePlugin-CLhvSrRp.d.mts} +1 -1
  6. package/dist/{DarkThemePlugin-GyV1mhmz.mjs → DarkThemePlugin-CqLyur3d.mjs} +1 -1
  7. package/dist/EventMediaPlugin-C2MC2ruT.mjs +1 -0
  8. package/dist/{EventMediaPlugin-oiYZdX1E.d.mts → EventMediaPlugin-DOIU5CZW.d.mts} +1 -1
  9. package/dist/{EventTooltipPlugin-BVStLFz2.d.mts → EventTooltipPlugin-BtU0ZtyW.d.mts} +1 -1
  10. package/dist/EventTooltipPlugin-Ch_xSklt.mjs +1 -0
  11. package/dist/{LightThemePlugin-ChMBy0bg.d.mts → LightThemePlugin-KFUJdO1V.d.mts} +1 -1
  12. package/dist/{LightThemePlugin-wHpjmbXj.mjs → LightThemePlugin-pQcTWONZ.mjs} +1 -1
  13. package/dist/PerformanceOverlayPlugin-BjEF-I5h.mjs +1 -0
  14. package/dist/{PerformanceOverlayPlugin-DFK_Clxo.d.mts → PerformanceOverlayPlugin-BmjL92Bg.d.mts} +1 -1
  15. package/dist/builtin-plugin/ContextMenuPlugin.d.mts +2 -2
  16. package/dist/builtin-plugin/ContextMenuPlugin.mjs +1 -1
  17. package/dist/builtin-plugin/DarkThemePlugin.d.mts +2 -2
  18. package/dist/builtin-plugin/DarkThemePlugin.mjs +1 -1
  19. package/dist/builtin-plugin/EventMediaPlugin.d.mts +2 -2
  20. package/dist/builtin-plugin/EventMediaPlugin.mjs +1 -1
  21. package/dist/builtin-plugin/EventTooltipPlugin.d.mts +2 -2
  22. package/dist/builtin-plugin/EventTooltipPlugin.mjs +1 -1
  23. package/dist/builtin-plugin/LightThemePlugin.d.mts +2 -2
  24. package/dist/builtin-plugin/LightThemePlugin.mjs +1 -1
  25. package/dist/builtin-plugin/MutexGuardPlugin.d.mts +1 -1
  26. package/dist/builtin-plugin/MutexGuardPlugin.mjs +1 -1
  27. package/dist/builtin-plugin/PerformanceOverlayPlugin.d.mts +2 -2
  28. package/dist/builtin-plugin/PerformanceOverlayPlugin.mjs +1 -1
  29. package/dist/index.d.mts +10 -11
  30. package/dist/index.mjs +1 -1
  31. package/dist/{types-CutIkjn4.d.mts → types-BovVI9wC.d.mts} +184 -36
  32. package/dist/types-Dc2ct_CM.mjs +1 -0
  33. package/package.json +53 -48
  34. package/dist/ContextMenuPlugin-6qe5xYCu.mjs +0 -1
  35. package/dist/EventMediaPlugin-CpPjf_yG.mjs +0 -1
  36. package/dist/EventTooltipPlugin-DNGXHYbN.mjs +0 -1
  37. package/dist/PerformanceOverlayPlugin-CcYPPPIx.mjs +0 -1
  38. package/dist/types-BvVk5Y9C.mjs +0 -1
@@ -74,6 +74,87 @@ interface ContextMenuStyle {
74
74
  borderWidth: number;
75
75
  minWidth: number;
76
76
  }
77
+ type TimelineLocale = "en" | "zh" | "zh-CN";
78
+ type TimelineMessageParams = Record<string, string | number>;
79
+ interface TimelineI18nMessages {
80
+ statusReady: string;
81
+ statusTrackAdded: string;
82
+ statusAtLeastOneTrackRequired: string;
83
+ statusTrackRemoved: string;
84
+ statusEmptyTrackRemoved: string;
85
+ statusEventUpdated: string;
86
+ statusEventDeleted: string;
87
+ statusDataLoaded: string;
88
+ statusTimeIndicatorMoved: string;
89
+ statusZoomChanged: string;
90
+ statusAutoFit: string;
91
+ statusAutoFitCappedWithPadding: string;
92
+ statusAutoFitCappedContentShort: string;
93
+ statusEndTimeUpdated: string;
94
+ statusInvalidSplitPosition: string;
95
+ statusEventSplit: string;
96
+ statusReadOnlyModeEnabled: string;
97
+ statusReadOnlyModeDisabled: string;
98
+ statusEventHighlighted: string;
99
+ statusHighlightCleared: string;
100
+ statusTimelineDestroyed: string;
101
+ statusDragging: string;
102
+ statusAutoTrackAdded: string;
103
+ statusEventPlaced: string;
104
+ statusEventSelected: string;
105
+ statusEventResizing: string;
106
+ statusEventResized: string;
107
+ statusReadOnlyEditDeleteBlocked: string;
108
+ statusReadOnlySplitBlocked: string;
109
+ statusHoverEventTimeRange: string;
110
+ labelTrackName: string;
111
+ labelDurationPrefix: string;
112
+ contextMenuEdit: string;
113
+ contextMenuDelete: string;
114
+ contextMenuExport: string;
115
+ overlayPerformanceMonitor: string;
116
+ overlayFps: string;
117
+ overlayLayerTimesMs: string;
118
+ overlayCollecting: string;
119
+ overlayLayerBackground: string;
120
+ overlayLayerTracks: string;
121
+ overlayLayerTimeline: string;
122
+ overlayLayerGuideLines: string;
123
+ overlayLayerIndicator: string;
124
+ overlayLayerScrollbar: string;
125
+ overlayLayerInteraction: string;
126
+ overlayLayerOverlay: string;
127
+ overlayLayerDragPreview: string;
128
+ errorCanvasNotFound: string;
129
+ errorCanvasContextUnavailable: string;
130
+ errorTimeIndicatorInvalid: string;
131
+ errorZoomLevelInvalid: string;
132
+ errorZoomLevelOutOfRange: string;
133
+ errorEndTimeInvalid: string;
134
+ errorEndTimeNotAfterStart: string;
135
+ warningEventsExceedEndTime: string;
136
+ errorInvalidTrackIndex: string;
137
+ errorInvalidTrackIndexWithValue: string;
138
+ errorInvalidEventIndex: string;
139
+ errorInvalidEventIndexWithValue: string;
140
+ errorInvalidDataFormat: string;
141
+ errorInvalidTimeRange: string;
142
+ errorStartTimeBeforeMin: string;
143
+ errorRenderLayer: string;
144
+ errorCoreLayerHook: string;
145
+ errorPluginDependenciesMissing: string;
146
+ errorPluginLoadFailed: string;
147
+ errorPluginLifecycleFailed: string;
148
+ errorPluginEventFailed: string;
149
+ errorPluginValidationFailed: string;
150
+ warningInvalidDraggingState: string;
151
+ warningDraggingEventMissing: string;
152
+ warningUnknownChangeType: string;
153
+ warningAlreadyInBatchMode: string;
154
+ warningNotInBatchMode: string;
155
+ warningEmptyHtmlTemplateFallback: string;
156
+ warningEmptyTooltipTemplateFallback: string;
157
+ }
77
158
  interface TimelineConfig {
78
159
  canvasHeight?: number;
79
160
  timelineHeight: number;
@@ -97,6 +178,9 @@ interface TimelineConfig {
97
178
  timeIndicatorSnapThreshold: number;
98
179
  timeIndicatorHeadSize: number;
99
180
  timeIndicatorTriangleHeight: number;
181
+ edgeScrollThrottle: number;
182
+ edgeScrollTriggerMargin: number;
183
+ edgeScrollViewportMargin: number;
100
184
  guideLineSnapThreshold: number;
101
185
  enableTimeIndicator: boolean;
102
186
  enableEventResize: boolean;
@@ -110,6 +194,7 @@ interface TimelineConfig {
110
194
  autoRemoveEmptyLastTrack: boolean;
111
195
  readOnly: boolean;
112
196
  showEventDurationLabel: boolean;
197
+ eventDurationPrefix: string;
113
198
  formatEventDuration: ((duration: number) => string) | null;
114
199
  /** 主刻度时间间隔(秒),>0。设置后启用自定义刻度模式 */
115
200
  scale: number | null;
@@ -123,6 +208,8 @@ interface TimelineConfig {
123
208
  contextMenuItems: ContextMenuItem[];
124
209
  contextMenuStyle: ContextMenuStyle;
125
210
  contextMenuHtml?: string | HTMLElement;
211
+ locale: TimelineLocale;
212
+ messages: TimelineI18nMessages;
126
213
  }
127
214
  interface TimelineCallbacks {
128
215
  onEventAdd?: ((data: EventAddData) => void) | null;
@@ -149,7 +236,7 @@ interface TimelineEvent {
149
236
  description: string;
150
237
  color: string;
151
238
  readonly?: boolean;
152
- customData?: Record<string, any>;
239
+ customData?: Record<string, unknown>;
153
240
  media?: {
154
241
  images?: Array<{
155
242
  src: string;
@@ -349,7 +436,7 @@ interface LoadDataFormat {
349
436
  description?: string;
350
437
  color?: string;
351
438
  readonly?: boolean;
352
- customData?: Record<string, any>;
439
+ customData?: Record<string, unknown>;
353
440
  media?: {
354
441
  images?: Array<{
355
442
  src: string;
@@ -368,6 +455,8 @@ interface LoadDataFormat {
368
455
  }
369
456
  interface TimelineOptions {
370
457
  canvasHeight?: number;
458
+ locale?: TimelineLocale;
459
+ messages?: Partial<TimelineI18nMessages>;
371
460
  startTime?: number;
372
461
  endTime?: number;
373
462
  startPaddingTime?: number;
@@ -384,6 +473,9 @@ interface TimelineOptions {
384
473
  snapToSeconds?: boolean;
385
474
  secondPrecisionZoomThreshold?: number;
386
475
  enableTimeIndicator?: boolean;
476
+ edgeScrollThrottle?: number;
477
+ edgeScrollTriggerMargin?: number;
478
+ edgeScrollViewportMargin?: number;
387
479
  enableEventResize?: boolean;
388
480
  enableEventSplit?: boolean;
389
481
  enableContextMenu?: boolean;
@@ -395,6 +487,7 @@ interface TimelineOptions {
395
487
  autoRemoveEmptyLastTrack?: boolean;
396
488
  readOnly?: boolean;
397
489
  showEventDurationLabel?: boolean;
490
+ eventDurationPrefix?: string;
398
491
  formatEventDuration?: (duration: number) => string;
399
492
  /** 主刻度时间间隔(秒),>0。设置后启用自定义刻度模式 */
400
493
  scale?: number;
@@ -430,6 +523,23 @@ declare global {
430
523
  }
431
524
  }
432
525
  //#endregion
526
+ //#region src/utils/time.d.ts
527
+ declare function formatTime(seconds: number, showSeconds?: boolean): string;
528
+ declare function getCurrentTime(): number;
529
+ //#endregion
530
+ //#region src/utils/i18n.d.ts
531
+ type TimelineMessageKey = keyof TimelineI18nMessages;
532
+ declare function createTimelineMessages(locale?: string, overrides?: Partial<TimelineI18nMessages>): TimelineI18nMessages;
533
+ //#endregion
534
+ //#region src/utils/performanceMonitor.d.ts
535
+ interface PerformanceStats {
536
+ average: number;
537
+ min: number;
538
+ max: number;
539
+ count: number;
540
+ total: number;
541
+ }
542
+ //#endregion
433
543
  //#region src/core/managers/ChangeScheduler.d.ts
434
544
  /**
435
545
  * 状态变更类型
@@ -446,21 +556,21 @@ declare class Timeline {
446
556
  private mouseHandler;
447
557
  private wheelHandler;
448
558
  private renderManager;
449
- private guideLinesCache;
450
- private guideLinesCacheTimestamp;
451
- private readonly GUIDE_LINES_CACHE_TTL;
452
559
  private eventListeners;
453
560
  private pluginManager;
454
561
  private logger;
455
562
  private errorHandler;
456
563
  private stateManager;
457
564
  private eventIndexManager;
565
+ private eventMutationService;
566
+ private guideLineService;
567
+ private hitTestService;
458
568
  private changeScheduler;
459
569
  private currentThemePluginId;
460
570
  constructor(canvasId: string, options?: TimelineOptions);
461
571
  getCanvas(): HTMLCanvasElement;
462
- usePlugin(plugin: any): Promise<boolean>;
463
- getLoadedPlugins(): any[];
572
+ usePlugin(plugin: TimelinePlugin): Promise<boolean>;
573
+ getLoadedPlugins(): TimelinePlugin[];
464
574
  isPluginLoaded(pluginName: string): boolean;
465
575
  removePlugin(pluginId: string): Promise<boolean>;
466
576
  setTheme(theme: "light" | "dark"): Promise<boolean>;
@@ -499,8 +609,7 @@ declare class Timeline {
499
609
  addTrack(): void;
500
610
  removeTrack(): void;
501
611
  autoRemoveEmptyLastTrack(): void;
502
- private validateEventTime;
503
- addEvent(trackIndex: number, startTime: number, endTime: number, title: string, description?: string, customData?: Record<string, any>, readonly?: boolean): void;
612
+ addEvent(trackIndex: number, startTime: number, endTime: number, title: string, description?: string, customData?: Record<string, unknown>, readonly?: boolean): void;
504
613
  updateEvent(trackIndex: number, eventIndex: number, updates: Partial<TimelineEvent>): boolean;
505
614
  updateEventData(trackIndex: number, eventIndex: number, eventData: {
506
615
  title?: string;
@@ -520,17 +629,7 @@ declare class Timeline {
520
629
  * - 使用节流的边界滚动而非每帧滚动
521
630
  */
522
631
  setTimeIndicatorDuringDrag(seconds: number): void;
523
- /** 上一次边界滚动的时间戳 */
524
632
  private _lastEdgeScrollTime;
525
- /** 边界滚动节流间隔(ms) */
526
- private static readonly EDGE_SCROLL_THROTTLE;
527
- /** 距画布边缘多少 px 内触发滚动 */
528
- private static readonly EDGE_SCROLL_MARGIN;
529
- /**
530
- * 拖拽过程中的节流边界滚动
531
- * 仅当指示器距离画布边缘 < EDGE_SCROLL_MARGIN 时才调整 scrollX,
532
- * 且受 EDGE_SCROLL_THROTTLE 节流。
533
- */
534
633
  private scrollToTimeIndicatorThrottled;
535
634
  private scrollToTimeIndicator;
536
635
  zoom(factor: number): void;
@@ -539,6 +638,7 @@ declare class Timeline {
539
638
  setEndTime(endTime: number): boolean;
540
639
  getEndTime(): number;
541
640
  formatTime(seconds: number): string;
641
+ t(key: TimelineMessageKey, params?: TimelineMessageParams): string;
542
642
  setStatus(text: string): void;
543
643
  getStatus(): string;
544
644
  /**
@@ -557,6 +657,10 @@ declare class Timeline {
557
657
  eventIndex: number;
558
658
  edge: "left" | "right";
559
659
  } | null;
660
+ private getEdgeScrollThrottle;
661
+ private getEdgeScrollTriggerMargin;
662
+ private getEdgeScrollViewportMargin;
663
+ private resolveEdgeScrollValue;
560
664
  calculateGuideLines(fromTrackIndex: number, eventIndex: number, toTrackIndex: number, newStartTime: number, duration: number): Array<{
561
665
  time: number;
562
666
  type: "start" | "end";
@@ -583,18 +687,14 @@ declare class Timeline {
583
687
  } | null;
584
688
  isReadOnly(): boolean;
585
689
  destroy(): void;
586
- }
587
- //#endregion
588
- //#region src/utils/performanceMonitor.d.ts
589
- interface PerformanceStats {
590
- average: number;
591
- min: number;
592
- max: number;
593
- count: number;
594
- total: number;
690
+ private clearGuideLineCache;
691
+ private getPluginId;
692
+ private isThemePlugin;
693
+ private redrawAfterPluginChange;
595
694
  }
596
695
  //#endregion
597
696
  //#region src/plugins/types.d.ts
697
+ type PluginLocalizedText = Partial<Record<TimelineLocale, string>>;
598
698
  declare enum PluginType {
599
699
  RENDER = "render",
600
700
  EVENT_HANDLER = "event_handler",
@@ -613,6 +713,7 @@ interface PluginMetadata {
613
713
  name: string;
614
714
  version: string;
615
715
  description: string;
716
+ descriptionI18n?: PluginLocalizedText;
616
717
  author?: string;
617
718
  type: PluginType;
618
719
  priority?: PluginPriority;
@@ -622,24 +723,71 @@ type RenderLayerPosition = "background" | "overlay";
622
723
  interface RenderLayer {
623
724
  name: string;
624
725
  position: RenderLayerPosition;
625
- render: (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, config: any, state: any) => void;
726
+ render: (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, config: TimelineConfig, state: TimelineState) => void;
626
727
  }
728
+ /**
729
+ * 可被插件钩子拦截的核心渲染层
730
+ */
731
+ type CoreRenderTarget = "tracks" | "timeline" | "guideLines" | "indicator" | "scrollbar" | "interaction";
732
+ /**
733
+ * 核心层钩子 — 允许插件拦截、修改或替换核心渲染层的行为
734
+ *
735
+ * `handler` 中调用 `next()` 执行默认渲染,不调用则完全替换默认行为。
736
+ * 多个钩子按注册顺序形成中间件链。
737
+ *
738
+ * @example
739
+ * ```ts
740
+ * // 修改轨道渲染:添加自定义背景后执行默认渲染
741
+ * registerCoreLayerHook({
742
+ * name: "custom-track-bg",
743
+ * target: "tracks",
744
+ * handler(ctx, canvas, config, state, next) {
745
+ * ctx.save();
746
+ * ctx.fillStyle = "rgba(0,0,255,0.05)";
747
+ * ctx.fillRect(0, 0, canvas.width, canvas.height);
748
+ * ctx.restore();
749
+ * next(); // 执行默认轨道渲染
750
+ * }
751
+ * });
752
+ *
753
+ * // 完全替换时间轴刻度渲染
754
+ * registerCoreLayerHook({
755
+ * name: "custom-timeline",
756
+ * target: "timeline",
757
+ * handler(ctx, canvas, config, state, _next) {
758
+ * // 不调用 next(),完全自定义绘制
759
+ * drawMyCustomTimeline(ctx, canvas, config, state);
760
+ * }
761
+ * });
762
+ * ```
763
+ */
764
+ interface CoreLayerHook {
765
+ /** 钩子名称,唯一标识 */
766
+ name: string;
767
+ /** 目标核心渲染层 */
768
+ target: CoreRenderTarget;
769
+ /** 钩子处理函数 */
770
+ handler: (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, config: TimelineConfig, state: TimelineState, next: () => void) => void;
771
+ }
772
+ type PluginEventHandler = (...args: unknown[]) => unknown;
627
773
  interface PluginAPI {
628
774
  registerRenderLayer: (layer: RenderLayer) => void;
629
775
  unregisterRenderLayer: (name: string) => void;
630
- registerEventHandler: (event: string, handler: Function) => void;
631
- unregisterEventHandler: (event: string, handler: Function) => void;
776
+ registerCoreLayerHook: (hook: CoreLayerHook) => void;
777
+ unregisterCoreLayerHook: (name: string) => void;
778
+ registerEventHandler: (event: string, handler: PluginEventHandler) => void;
779
+ unregisterEventHandler: (event: string, handler: PluginEventHandler) => void;
632
780
  showNotification: (message: string, type?: "info" | "warning" | "error") => void;
633
- getData: (key: string) => any;
634
- setData: (key: string, value: any) => void;
781
+ getData: (key: string) => unknown;
782
+ setData: (key: string, value: unknown) => void;
635
783
  setPerformanceProvider: (provider: PerformanceProvider) => void;
636
784
  getPerformanceStats: () => Map<string, PerformanceStats>;
637
785
  getFPS: () => number;
638
786
  }
639
787
  interface PluginContext {
640
788
  timeline: Timeline;
641
- config: any;
642
- state: any;
789
+ config: TimelineConfig;
790
+ state: TimelineState;
643
791
  api: PluginAPI;
644
792
  }
645
793
  interface TimelinePlugin {
@@ -656,4 +804,4 @@ interface PerformanceProvider {
656
804
  getFPS: () => number;
657
805
  }
658
806
  //#endregion
659
- export { Track as C, TimelineState as S, TimelineCallbacks as _, ContextMenuItem as a, TimelineEvent as b, EventClickData as c, EventMoveData as d, EventTextStyle as f, TimeIndicatorMoveData as g, LoadDataFormat as h, ContextMenuData as i, EventDeleteData as l, InteractionTarget as m, Timeline as n, ContextMenuStyle as o, EventUpdateData as p, ChangeType as r, EventAddData as s, TimelinePlugin as t, EventEditData as u, TimelineColors as v, ZoomData as w, TimelineOptions as x, TimelineConfig as y };
807
+ export { TimelineCallbacks as A, ZoomData as B, EventEditData as C, InteractionTarget as D, EventUpdateData as E, TimelineLocale as F, TimelineMessageParams as I, TimelineOptions as L, TimelineConfig as M, TimelineEvent as N, LoadDataFormat as O, TimelineI18nMessages as P, TimelineState as R, EventDeleteData as S, EventTextStyle as T, ContextMenuData as _, PluginLocalizedText as a, EventAddData as b, PluginType as c, TimelinePlugin as d, Timeline as f, getCurrentTime as g, formatTime as h, PluginContext as i, TimelineColors as j, TimeIndicatorMoveData as k, RenderLayer as l, createTimelineMessages as m, CoreRenderTarget as n, PluginMetadata as o, ChangeType as p, PluginAPI as r, PluginPriority as s, CoreLayerHook as t, RenderLayerPosition as u, ContextMenuItem as v, EventMoveData as w, EventClickData as x, ContextMenuStyle as y, Track as z };
@@ -0,0 +1 @@
1
+ let e=function(e){return e.RENDER=`render`,e.EVENT_HANDLER=`event_handler`,e.DATA_SOURCE=`data_source`,e.THEME=`theme`,e.TOOL=`tool`,e.EXTENSION=`extension`,e}({}),t=function(e){return e[e.LOW=0]=`LOW`,e[e.NORMAL=50]=`NORMAL`,e[e.HIGH=100]=`HIGH`,e[e.CRITICAL=200]=`CRITICAL`,e}({});export{e as n,t};
package/package.json CHANGED
@@ -1,48 +1,53 @@
1
- {
2
- "name": "timeline-canvas",
3
- "version": "1.3.0",
4
- "description": "A powerful timeline component built with Canvas and TypeScript",
5
- "main": "dist/index.mjs",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.mts",
8
- "type": "module",
9
- "scripts": {
10
- "build": "tsdown",
11
- "dev": "tsdown --watch",
12
- "typecheck": "tsc -p tsconfig.json --noEmit",
13
- "lint": "oxlint src",
14
- "test": "echo \"Error: no test specified\" && exit 1"
15
- },
16
- "keywords": [
17
- "timeline",
18
- "canvas",
19
- "typescript",
20
- "event-timeline",
21
- "visual-timeline"
22
- ],
23
- "author": "sky <https://github.com/umbrella22>",
24
- "license": "MIT",
25
- "packageManager": "pnpm@10.18.1",
26
- "devDependencies": {
27
- "@types/node": "^24.10.1",
28
- "oxlint": "^1.43.0"
29
- },
30
- "dependencies": {
31
- "immer": "^10.1.1"
32
- },
33
- "files": [
34
- "dist",
35
- "README.md",
36
- "README_CN.md"
37
- ],
38
- "exports": {
39
- ".": {
40
- "types": "./dist/index.d.mts",
41
- "import": "./dist/index.mjs"
42
- },
43
- "./builtin-plugin/*": {
44
- "types": "./dist/builtin-plugin/*.d.mts",
45
- "import": "./dist/builtin-plugin/*.mjs"
46
- }
47
- }
48
- }
1
+ {
2
+ "name": "timeline-canvas",
3
+ "version": "1.4.0",
4
+ "description": "A powerful timeline component built with Canvas and TypeScript",
5
+ "main": "dist/index.mjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.mts",
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsdown",
11
+ "dev": "tsdown --watch",
12
+ "typecheck": "tsc -p tsconfig.json --noEmit",
13
+ "lint": "oxlint src tests vitest.config.ts",
14
+ "test": "vitest",
15
+ "test:run": "vitest run",
16
+ "test:coverage": "vitest run --coverage"
17
+ },
18
+ "keywords": [
19
+ "timeline",
20
+ "canvas",
21
+ "typescript",
22
+ "event-timeline",
23
+ "visual-timeline"
24
+ ],
25
+ "author": "sky <https://github.com/umbrella22>",
26
+ "license": "MIT",
27
+ "packageManager": "pnpm@10.18.1",
28
+ "devDependencies": {
29
+ "@types/node": "^24.10.1",
30
+ "@vitest/coverage-v8": "^4.1.2",
31
+ "jsdom": "^29.0.1",
32
+ "oxlint": "^1.43.0",
33
+ "vitest": "^4.1.2"
34
+ },
35
+ "dependencies": {
36
+ "immer": "^10.1.1"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "README_CN.md"
42
+ ],
43
+ "exports": {
44
+ ".": {
45
+ "types": "./dist/index.d.mts",
46
+ "import": "./dist/index.mjs"
47
+ },
48
+ "./builtin-plugin/*": {
49
+ "types": "./dist/builtin-plugin/*.d.mts",
50
+ "import": "./dist/builtin-plugin/*.mjs"
51
+ }
52
+ }
53
+ }
@@ -1 +0,0 @@
1
- import{i as e}from"./Logger-CfVK3yFL.mjs";import{t}from"./types-BvVk5Y9C.mjs";const n=e(`ContextMenuPlugin`);function r(e={}){return{metadata:{name:`context-menu`,version:`1.0.0`,description:`Context menu as plugin, supports HTML takeover`,type:t.EXTENSION},activate(t){let r=t.config&&t.config.contextMenuHtml||void 0,i=e.htmlTemplate||(typeof r==`string`?r:void 0),a=!!i;a&&(!i||i.trim()===``)&&(n.warn(`htmlTemplate is empty, falling back to Canvas rendering`),a=!1),t.api.registerRenderLayer({name:`context-menu-overlay`,position:`overlay`,render(e,n,r,o){if(!r.enableContextMenu||!o.contextMenuVisible||!o.contextMenuEvent){let e=t.api.getData(`contextMenuContainer`)||null;e&&(e.style.display=`none`),o.contextMenuBounds=null;return}let{padding:s,itemHeight:c,borderRadius:l,borderWidth:u,minWidth:d,fontSize:f,fontFamily:p,fontWeight:m}=r.contextMenuStyle;e.save(),e.font=`${m} ${f}px ${p}`;let h=d,g=`${m}-${f}-${p}`,_=t.api.getData(`contextMenuTextWidthCache`)||{},v=_[g]||{};for(let t of r.contextMenuItems){let n=v[t.name],r=n===void 0?e.measureText(t.name).width:n;n===void 0&&(v[t.name]=r),h=Math.max(h,r+s*2)}_[g]=v,t.api.setData(`contextMenuTextWidthCache`,_);let y=h,b=r.contextMenuItems.length*c+s*2,x=t.timeline.getCanvas().getBoundingClientRect(),S=o.contextMenuX,C=o.contextMenuY;if(S+y>x.width&&(S=x.width-y-5),C+b>x.height&&(C=x.height-b-5),o.contextMenuBounds={x:S,y:C,width:y,height:b,itemHeight:c,padding:s},a){let n=t.api.getData(`contextMenuContainer`)||null;if(!n){n=document.createElement(`div`),n.style.position=`absolute`,n.style.zIndex=`1000`,n.style.boxShadow=`0 2px 10px rgba(0,0,0,0.3)`,n.style.border=`${u}px solid ${r.colors.contextMenuBorder}`,n.style.borderRadius=`${l}px`,n.style.font=`${m} ${f}px ${p}`,n.style.background=r.colors.contextMenuBackground,n.style.color=r.colors.contextMenuText;let e=t.timeline.getCanvas().parentElement||document.body;e.style.position=e.style.position||`relative`,e.appendChild(n),t.api.setData(`contextMenuContainer`,n)}if(n.style.display=`block`,n.style.left=`${S}px`,n.style.top=`${C}px`,n.style.width=`${y}px`,n.style.height=`${b}px`,i)n.innerHTML=i;else{let e=``;for(let t=0;t<r.contextMenuItems.length;t++){let n=r.contextMenuItems[t],i=o.hoveredContextMenuItem===t,a=i?r.colors.contextMenuHoverBackground:`transparent`,l=i?r.colors.contextMenuHoverText:r.colors.contextMenuText;e+=`<div style="height:${c}px; padding:${s}px; background:${a}; color:${l}; display:flex; align-items:center;">${n.name}</div>`}n.innerHTML=e}e.restore();return}e.shadowColor=`rgba(0, 0, 0, 0.3)`,e.shadowBlur=10,e.shadowOffsetX=0,e.shadowOffsetY=2,e.fillStyle=r.colors.contextMenuBackground,e.beginPath(),e.roundRect?e.roundRect(S,C,y,b,l):(e.moveTo(S+l,C),e.lineTo(S+y-l,C),e.quadraticCurveTo(S+y,C,S+y,C+l),e.lineTo(S+y,C+b-l),e.quadraticCurveTo(S+y,C+b,S+y-l,C+b),e.lineTo(S+l,C+b),e.quadraticCurveTo(S,C+b,S,C+b-l),e.lineTo(S,C+l),e.quadraticCurveTo(S,C,S+l,C)),e.fill(),e.shadowColor=`transparent`,e.strokeStyle=r.colors.contextMenuBorder,e.lineWidth=u,e.stroke(),e.font=`${m} ${f}px ${p}`;for(let t=0;t<r.contextMenuItems.length;t++){let n=r.contextMenuItems[t],i=C+s+t*c;o.hoveredContextMenuItem===t&&(e.fillStyle=r.colors.contextMenuHoverBackground,e.beginPath(),t===0?e.roundRect?e.roundRect(S+u,i,y-u*2,c,[l-u,l-u,0,0]):e.fillRect(S+u,i,y-u*2,c):t===r.contextMenuItems.length-1&&e.roundRect?e.roundRect(S+u,i,y-u*2,c,[0,0,l-u,l-u]):e.fillRect(S+u,i,y-u*2,c),e.fill()),e.fillStyle=o.hoveredContextMenuItem===t?r.colors.contextMenuHoverText:r.colors.contextMenuText,e.textAlign=`left`,e.textBaseline=`middle`,e.fillText(n.name,S+s,i+c/2)}e.restore()}})},deactivate(e){e.api.unregisterRenderLayer(`context-menu-overlay`);let t=e.api.getData(`contextMenuContainer`)||null;t&&t.parentElement&&t.parentElement.removeChild(t)},destroy(e){let t=e.api.getData(`contextMenuContainer`)||null;t&&t.parentElement&&t.parentElement.removeChild(t)}}}export{r as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./types-BvVk5Y9C.mjs";function t(e,t=!0){let n=Math.round(e),r=Math.floor(n/3600),i=Math.floor(n%3600/60),a=n%60;return t?`${r.toString().padStart(2,`0`)}:${i.toString().padStart(2,`0`)}:${a.toString().padStart(2,`0`)}`:`${r.toString().padStart(2,`0`)}:${i.toString().padStart(2,`0`)}`}function n(){let e=new Date;return e.getHours()*3600+e.getMinutes()*60+e.getSeconds()}function r(e,t,n,r,i,a){if(i!=null&&i>0){let t=i/Math.max(1,Math.floor(a??10));return e>=r?t:i}return!n||e<r?t*60:[{threshold:10,seconds:.5},{threshold:8,seconds:1},{threshold:5,seconds:2},{threshold:3,seconds:5},{threshold:2,seconds:10},{threshold:1.5,seconds:15},{threshold:0,seconds:30}].find(t=>e>=t.threshold)?.seconds??30}function i(e,t){return Math.round(e/t)*t}function a(e,t=3){let n=10**t;return Math.round(e*n)/n}function o(e,n){return`${t(e)} - ${t(n)}`}function s(e){return`持续 ${t(a(e))}`}function c(e,t,n,r,i,a,o=!1){e.beginPath(),e.moveTo(t+a,n),e.lineTo(t+r-a,n),e.arcTo(t+r,n,t+r,n+a,a),e.lineTo(t+r,n+i-a),e.arcTo(t+r,n+i,t+r-a,n+i,a),e.lineTo(t+a,n+i),e.arcTo(t,n+i,t,n+i-a,a),e.lineTo(t,n+a),e.arcTo(t,n,t+a,n,a),e.closePath(),o?e.stroke():e.fill()}function l(e,t,n,r,i,a){return n+(e-t)*r*i-a}const u={canvasBackground:`#1E1E2E`,timelineBackground:`#2D2D3D`,trackBackground:`#2A2A3A`,trackBackgroundSelected:`#3A3A4A`,timelineText:`#CCCCDD`,timelineGrid:`#5A5A7A`,timelineSubGrid:`#3A3A4A`,trackText:`#CCCCDD`,eventColors:[`#FF6B6B`,`#4ECDC4`,`#45B7D1`,`#96CEB4`,`#FECA57`,`#FF9FF3`],eventText:`#FFFFFF`,eventBorder:`#FFFFFF`,eventBorderSelected:`#FFD700`,eventOverlay:`rgba(255, 255, 255, 0.2)`,dragPreviewValid:`rgba(100, 255, 100, 0.5)`,dragPreviewInvalid:`rgba(255, 100, 100, 0.5)`,dragPreviewBorderValid:`#00FF00`,dragPreviewBorderInvalid:`#FF0000`,timeIndicator:`#FF6B6B`,guideLine:`#00D9FF`,guideLineLabel:`#00D9FF`,dragTimeReferenceLine:`#FFD700`,dragTimeReferenceLabel:`#FFD700`,dragTimeReferenceLabelBackground:`rgba(0, 0, 0, 0)`,scrollbarTrack:`rgba(255, 255, 255, 0.6)`,scrollbarHandle:`rgba(150, 150, 150, 0.8)`,scrollbarHandleHover:`rgba(0, 0, 0, 0.85)`,scrollbarHandleHighlight:`rgba(255, 255, 255, 0.3)`,scrollbarBorder:`rgba(255, 255, 255, 0.9)`,contextMenuBackground:`#2D2D3D`,contextMenuBorder:`#5A5A7A`,contextMenuText:`#CCCCDD`,contextMenuHoverBackground:`#3A3A4A`,contextMenuHoverText:`#FFFFFF`,eventDurationLabel:`#FFD700`},d={titleFontSize:`auto`,timeFontSize:`auto`,titleFontFamily:`Arial`,timeFontFamily:`Arial`,titleFontWeight:`bold`,timeFontWeight:`normal`,titleColor:null,timeColor:null,textAlign:`left`,verticalAlign:`middle`,titleOffsetY:0,timeOffsetY:0,showTitle:!0,showTime:!0,minHeightForTitle:40,minHeightForTime:50},f={borderRadius:2,enableSelectionGlow:!1,selectionGlowBlur:10},p=[{type:`edit`,name:`编辑`},{type:`delete`,name:`删除`},{type:`export`,name:`导出`}],m={fontSize:14,fontFamily:`Arial, sans-serif`,fontWeight:`normal`,padding:8,itemHeight:32,borderRadius:6,borderWidth:1,minWidth:120},h={timelineHeight:40,trackHeight:80,trackMargin:10,firstTrackTopMargin:10,secondWidth:100/3600,startTime:0,endTime:86400,startPaddingTime:.5,endPaddingTime:0,autoFitOnInit:!0,minAutoFitZoom:1,maxAutoFitZoom:3,timeUnit:`second`,timeFormat:`24h`,snapInterval:15,snapToSeconds:!0,secondPrecisionZoomThreshold:1.5,timeIndicatorWidth:3,timeIndicatorSnapThreshold:10,timeIndicatorHeadSize:12,timeIndicatorTriangleHeight:8,guideLineSnapThreshold:1,enableTimeIndicator:!0,enableEventResize:!0,enableEventSplit:!0,enableContextMenu:!0,resizeHandleWidth:8,minEventDuration:.25,debug:!1,enablePerformanceMonitor:!1,autoAddTrack:!0,autoRemoveEmptyLastTrack:!0,readOnly:!1,showEventDurationLabel:!0,formatEventDuration:null,scale:null,scaleSplitCount:10,getScaleRender:null};function g(e){return{id:e.id,startTime:e.startTime,endTime:e.endTime,duration:e.duration,title:e.title,description:e.description,color:e.color,...e.readonly?{readonly:e.readonly}:{},...e.customData?{customData:{...e.customData}}:{}}}const _=new Map,v=new Map;function y(e,t,n,r){let i=_.get(e);if(i)return i;let a=new Path2D,o=r/2,s=Math.max(1,Math.floor((r-2)/2)),c=t.length,l=Math.max(1,n-1);for(let e=0;e<n;e++){let n=Math.min(c-1,Math.max(0,Math.floor(e/l*c))),r=Math.max(-1,Math.min(1,t[n]))*s;a.moveTo(e,o-r),a.lineTo(e,o+r)}return _.set(e,a),a}function b(e,t,n,r,i,a){let o=v.get(e);if(o&&o.width===n&&o.height===r)return o.bitmap;if(typeof OffscreenCanvas>`u`||n<=0||r<=0)return null;try{let o=Math.max(1,Math.ceil(n)),s=Math.max(1,Math.ceil(r)),c=new OffscreenCanvas(o,s),l=c.getContext(`2d`);if(!l)return null;a&&(l.fillStyle=a,l.fillRect(0,0,o,s));let u=y(`${e}_path`,t,o,s);l.strokeStyle=i,l.lineWidth=1,l.stroke(u);let d=c.transferToImageBitmap(),f=v.get(e);return f&&f.bitmap.close(),v.set(e,{bitmap:d,width:o,height:s}),d}catch{return null}}function x(){return{metadata:{name:`event-media`,version:`1.0.0`,description:`Render images and waveforms inside event blocks`,type:e.RENDER},async activate(e){let t=new Map,n=new Map,r=new Map;e.api.setData(`eventMediaImageCache`,t),e.api.setData(`eventMediaImageLoading`,n),e.api.setData(`eventMediaWaveCache`,r);let i=(t,n,r,i,a,o,s,l,u,d)=>{let f=e.api.getData(`eventMediaImageCache`),p=e.api.getData(`eventMediaImageLoading`),m=e.api.getData(`eventMediaWaveCache`),h=r.tracks[i].events[a],g=s+u,_=n.eventBlockStyle.borderRadius;t.save(),_>0?(c(t,o,g,l,d,_),t.clip()):(t.beginPath(),t.rect(o,g,l,d),t.clip());let v=h.media&&h.media.images||[];if(v.length>0&&f&&p)for(let e of v){let n=`${h.id??`${i}_${a}`}_${e.src}`,r=f.get(n);if(!r&&!p.get(n)){let t=fetch(e.src).then(e=>e.blob()).then(e=>createImageBitmap(e)).then(e=>(f.set(n,e),e)).catch(()=>void 0);p.set(n,t)}if(r=f.get(n),r){let n=e.fit||`cover`,i=e.opacity===void 0?.35:e.opacity,a=l,s=d;if(n!==`stretch`){let e=r.width,t=r.height,i=n===`cover`?Math.max(l/e,d/t):Math.min(l/e,d/t);a=Math.max(1,Math.floor(e*i)),s=Math.max(1,Math.floor(t*i))}let c=o+(l-a)/2,u=g+(d-s)/2,f=t.globalAlpha;t.globalAlpha=i,t.drawImage(r,0,0,r.width,r.height,c,u,a,s),t.globalAlpha=f}}let x=h.media&&h.media.waveform||void 0;if(x){let e=`${h.id??`${i}_${a}`}_wf`,n=m?m.get(e):void 0;if(!n){let t=x.data;n=Array.isArray(t)?new Float32Array(t):t,m&&m.set(e,n)}let r=x.opacity===void 0?.5:x.opacity,s=t.globalAlpha;t.globalAlpha=r;let c=x.color||`#00A0FF`,u=Math.max(1,Math.ceil(l)),f=Math.max(1,Math.ceil(d)),p=b(`${e}_${u}_${f}_${c}_${x.backgroundColor||``}`,n,u,f,c,x.backgroundColor);if(p)t.drawImage(p,o,g,l,d);else{x.backgroundColor&&(t.fillStyle=x.backgroundColor,t.fillRect(o,g,l,d));let r=y(`${e}_${u}_${f}`,n,u,f);t.save(),t.translate(o,g),t.strokeStyle=c,t.lineWidth=1,t.stroke(r),t.restore()}t.globalAlpha=s}t.restore()};e.api.registerEventHandler(`render:event:media`,i),e.api.setData(`eventMediaHandler`,i)},deactivate(e){let t=e.api.getData(`eventMediaHandler`);t&&e.api.unregisterEventHandler(`render:event:media`,t);let n=e.api.getData(`eventMediaImageCache`);n&&n.clear();let r=e.api.getData(`eventMediaImageLoading`);r&&r.clear();let i=e.api.getData(`eventMediaWaveCache`);i&&i.clear();for(let e of v.values())e.bitmap.close();v.clear(),_.clear()}}}export{i as _,p as a,d as c,a as d,s as f,r as g,n as h,h as i,c as l,o as m,g as n,m as o,t as p,u as r,f as s,x as t,l as u};
@@ -1 +0,0 @@
1
- import{i as e}from"./Logger-CfVK3yFL.mjs";import{t}from"./types-BvVk5Y9C.mjs";const n=e(`EventTooltipPlugin`);function r(e={}){let{htmlTemplate:r,showDelay:i=300,maxWidth:a=300,padding:o=8,borderRadius:s=4,backgroundColor:c=`#333`,textColor:l=`#fff`,borderColor:u=`#555`,fontSize:d=12,fontFamily:f=`Arial, sans-serif`}=e,p={visible:!1,title:``,x:0,y:0,trackIndex:-1,eventIndex:-1},m=null,h=null;function g(e,t,n){return e.measureText(t).width>n}return{metadata:{name:`event-tooltip`,version:`1.0.0`,description:`Tooltip plugin for displaying full event title when text is truncated`,type:t.EXTENSION},activate(e){let t=e.timeline.getCanvas(),_=!1,v=null,y=n=>{let r=t.getBoundingClientRect(),a=n.clientX-r.left,o=n.clientY-r.top,s=e.config,c=e.state,l=e.timeline.getEventAtPosition(a,o);if(l){let{trackIndex:n,eventIndex:r}=l,u=c.tracks[n]?.events[r];if(!u){x();return}if(h&&h.trackIndex===n&&h.eventIndex===r&&p.visible){p.x=a,p.y=o,e.timeline.draw();return}h={trackIndex:n,eventIndex:r},m&&=(clearTimeout(m),null);let d=u.duration*s.secondWidth*c.zoomLevel-20,f=t.getContext(`2d`);if(!f)return;let _=s.eventTextStyle,v=_.titleFontSize===`auto`?Math.max(10,Math.min(14,s.trackHeight*.175)):_.titleFontSize;f.font=`${_.titleFontWeight} ${v}px ${_.titleFontFamily}`,g(f,u.title,d)?m=setTimeout(()=>{p={visible:!0,title:u.title,x:a,y:o,trackIndex:n,eventIndex:r},e.timeline.draw()},i):x()}else x()},b=e=>{v=e,_||(_=!0,requestAnimationFrame(()=>{_=!1,v&&=(y(v),null)}))},x=()=>{m&&=(clearTimeout(m),null),h=null,p.visible&&(p.visible=!1,e.timeline.draw())},S=()=>{x()};t.addEventListener(`mousemove`,b),t.addEventListener(`mouseleave`,S),e.api.setData(`tooltipMouseMoveHandler`,b),e.api.setData(`tooltipMouseLeaveHandler`,S),e.api.registerRenderLayer({name:`event-tooltip-overlay`,position:`overlay`,render(t,i,m,h){if(!p.visible||!p.title){let t=e.api.getData(`tooltipContainer`)||null;t&&(t.style.display=`none`);return}let g=e.timeline.getCanvas().getBoundingClientRect(),_=p.x,v=p.y-10;t.save(),t.font=`${d}px ${f}`;let y=p.title.split(``),b=[],x=``;for(let e of y){let n=x+e;t.measureText(n).width>a-o*2?x?(b.push(x),x=e):b.push(e):x=n}x&&b.push(x);let S=0;for(let e of b){let n=t.measureText(e).width;S=Math.max(S,n)}S=Math.min(S+o*2,a);let C=d*1.4,w=b.length*C+o*2;if(v-=w,_+S>g.width&&(_=g.width-S-5),_<5&&(_=5),v<5&&(v=p.y+20),r){let i=r(p.title);if(!i||i.trim()===``)n.warn(`htmlTemplate returned empty content, falling back to Canvas rendering`);else{let n=e.api.getData(`tooltipContainer`)||null;if(!n){n=document.createElement(`div`),n.style.position=`absolute`,n.style.zIndex=`1001`,n.style.pointerEvents=`none`,n.style.boxShadow=`0 2px 8px rgba(0,0,0,0.3)`,n.style.border=`1px solid ${u}`,n.style.borderRadius=`${s}px`,n.style.padding=`${o}px`,n.style.fontSize=`${d}px`,n.style.fontFamily=f,n.style.backgroundColor=c,n.style.color=l,n.style.maxWidth=`${a}px`,n.style.wordWrap=`break-word`,n.style.whiteSpace=`pre-wrap`;let t=e.timeline.getCanvas().parentElement||document.body;t.style.position=t.style.position||`relative`,t.appendChild(n),e.api.setData(`tooltipContainer`,n)}n.style.display=`block`,n.style.left=`${_}px`,n.style.top=`${v}px`,n.innerHTML=i,t.restore();return}}if(t.shadowColor=`rgba(0, 0, 0, 0.3)`,t.shadowBlur=8,t.shadowOffsetX=0,t.shadowOffsetY=2,t.fillStyle=c,t.beginPath(),t.roundRect)t.roundRect(_,v,S,w,s);else{let e=_,n=v,r=S,i=w,a=s;t.moveTo(e+a,n),t.lineTo(e+r-a,n),t.quadraticCurveTo(e+r,n,e+r,n+a),t.lineTo(e+r,n+i-a),t.quadraticCurveTo(e+r,n+i,e+r-a,n+i),t.lineTo(e+a,n+i),t.quadraticCurveTo(e,n+i,e,n+i-a),t.lineTo(e,n+a),t.quadraticCurveTo(e,n,e+a,n)}t.fill(),t.shadowColor=`transparent`,t.shadowBlur=0,t.strokeStyle=u,t.lineWidth=1,t.stroke(),t.fillStyle=l,t.font=`${d}px ${f}`,t.textAlign=`left`,t.textBaseline=`top`;for(let e=0;e<b.length;e++)t.fillText(b[e],_+o,v+o+e*C);t.restore()}})},deactivate(e){let t=e.timeline.getCanvas(),n=e.api.getData(`tooltipMouseMoveHandler`),r=e.api.getData(`tooltipMouseLeaveHandler`);n&&t.removeEventListener(`mousemove`,n),r&&t.removeEventListener(`mouseleave`,r),m&&=(clearTimeout(m),null),e.api.unregisterRenderLayer(`event-tooltip-overlay`);let i=e.api.getData(`tooltipContainer`)||null;i&&i.parentElement&&i.parentElement.removeChild(i)},destroy(e){let t=e.api.getData(`tooltipContainer`)||null;t&&t.parentElement&&t.parentElement.removeChild(t),m&&=(clearTimeout(m),null)}}}export{r as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./performanceMonitor-BBherdD8.mjs";import{t}from"./types-BvVk5Y9C.mjs";const n={metadata:{name:`performance-overlay`,version:`1.0.0`,description:`Draw performance metrics overlay`,type:t.TOOL},activate(t){let n=new e(t.config.enablePerformanceMonitor||t.config.debug);t.api.setPerformanceProvider(n),t.api.setData(`perfMonitor`,n),t.api.setData(`perfOverlayPos`,{x:10,y:10}),t.api.setData(`perfOverlayDragging`,!1),t.api.setData(`perfOverlayOffset`,{x:0,y:0});let r=t.timeline.getCanvas(),i=e=>{let n=r.getBoundingClientRect(),i=e.clientX-n.left,a=e.clientY-n.top,o=t.api.getData(`perfOverlaySize`),s=t.api.getData(`perfOverlayPos`)||{x:10,y:10};o&&i>=s.x&&i<=s.x+o.width&&a>=s.y&&a<=s.y+o.height&&(t.api.setData(`perfOverlayDragging`,!0),t.api.setData(`perfOverlayOffset`,{x:i-s.x,y:a-s.y}),e.stopImmediatePropagation(),e.preventDefault())},a=e=>{if(!t.api.getData(`perfOverlayDragging`))return;let n=r.getBoundingClientRect(),i=e.clientX-n.left,a=e.clientY-n.top,o=t.api.getData(`perfOverlayOffset`)||{x:0,y:0},s=t.api.getData(`perfOverlaySize`)||{width:0,height:0},c=i-o.x,l=a-o.y;c=Math.max(0,Math.min(n.width-s.width,c)),l=Math.max(0,Math.min(n.height-s.height,l)),t.api.setData(`perfOverlayPos`,{x:c,y:l}),t.timeline.markDirty([`overlay`]),t.timeline.draw(),e.stopImmediatePropagation(),e.preventDefault()},o=e=>{t.api.getData(`perfOverlayDragging`)&&t.api.setData(`perfOverlayDragging`,!1)};r.addEventListener(`mousedown`,i,{capture:!0}),r.addEventListener(`mousemove`,a,{capture:!0}),window.addEventListener(`mouseup`,o,{capture:!0}),t.api.setData(`perfOverlayListeners`,{onMouseDown:i,onMouseMove:a,onMouseUp:o}),t.api.registerRenderLayer({name:`performance-overlay`,position:`overlay`,render(e){let n=t.api.getData(`perfMonitor`),r=!!(t.config.enablePerformanceMonitor||t.config.debug);if(n)if(r)n.enable();else{n.disable(),n.clear();return}else if(!r)return;let i=t.api.getPerformanceStats(),a=t.api.getFPS(),o=Array.from(i.entries()),s=[`background`,`tracks`,`timeline`,`interaction`,`guideLines`,`scrollbar`,`overlay`,`dragPreview`],c=t.timeline.getLastLayerTimes?t.timeline.getLastLayerTimes():void 0,l=c?s.filter(e=>c[e]!==void 0).length:0,u=c?1+l:0,d=25+(1+(i.size===0?1:o.length)+u)*20+30,f=t.api.getData(`perfOverlayPos`)||{x:10,y:10};t.api.setData(`perfOverlaySize`,{width:280,height:d}),e.fillStyle=`rgba(0, 0, 0, 0.8)`,e.fillRect(f.x,f.y,280,d),e.strokeStyle=`rgba(100, 255, 100, 0.6)`,e.lineWidth=2,e.strokeRect(f.x,f.y,280,d),e.fillStyle=`#00FF00`,e.font=`bold 16px monospace`,e.textAlign=`left`,e.fillText(`性能监控报告`,f.x+10,f.y+20);let p=f.y+20+25;if(e.fillStyle=a>=55?`#00FF00`:a>=30?`#FFFF00`:`#FF0000`,e.font=`bold 14px monospace`,e.fillText(`FPS: ${a.toFixed(1)}`,f.x+10,p),e.font=`12px monospace`,p+=20,c){e.fillStyle=`#00FFFF`,e.font=`bold 12px monospace`,e.fillText(`Layer Times (ms):`,f.x+10,p),p+=20,e.font=`12px monospace`,e.fillStyle=`#FFFFFF`;for(let t of s)c[t]!==void 0&&(e.fillText(`${t}: ${c[t].toFixed(2)}`,f.x+10,p),p+=20)}i.size===0?(e.fillStyle=`#FFFFFF`,e.fillText(`Collecting...`,f.x+10,p),p+=20):o.forEach(([t,n])=>{let r=`#00FF00`;n.average>16&&(r=`#FFFF00`),n.average>33&&(r=`#FF0000`),e.fillStyle=r;let i=`${t}: ${n.average.toFixed(2)}ms (${n.min.toFixed(1)}-${n.max.toFixed(1)})`;e.fillText(i,f.x+10,p),p+=20})}})},deactivate(e){e.api.unregisterRenderLayer(`performance-overlay`);let t=e.api.getData(`perfOverlayListeners`),n=e.timeline.getCanvas();t&&(n.removeEventListener(`mousedown`,t.onMouseDown,!0),n.removeEventListener(`mousemove`,t.onMouseMove,!0),window.removeEventListener(`mouseup`,t.onMouseUp,!0))}};export{n as t};
@@ -1 +0,0 @@
1
- let e=function(e){return e.RENDER=`render`,e.EVENT_HANDLER=`event_handler`,e.DATA_SOURCE=`data_source`,e.THEME=`theme`,e.TOOL=`tool`,e.EXTENSION=`extension`,e}({});export{e as t};