@363045841yyt/klinechart-core 0.9.0-alpha.2 → 0.9.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/createChartController.d.ts.map +1 -1
- package/dist/controllers/createChartController.js +9 -8
- package/dist/controllers/createChartController.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/types.d.ts +5 -0
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/data/binance.d.ts +1 -1
- package/dist/data/binance.d.ts.map +1 -1
- package/dist/data/binance.js +1 -1
- package/dist/data/binance.js.map +1 -1
- package/dist/engine/chart.d.ts +9 -9
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +69 -96
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/chartTypes.d.ts +5 -0
- package/dist/engine/chartTypes.d.ts.map +1 -1
- package/dist/engine/controller/interaction.d.ts +4 -2
- package/dist/engine/controller/interaction.d.ts.map +1 -1
- package/dist/engine/controller/interaction.js +18 -10
- package/dist/engine/controller/interaction.js.map +1 -1
- package/dist/engine/data/chartDataManager.d.ts +12 -18
- package/dist/engine/data/chartDataManager.d.ts.map +1 -1
- package/dist/engine/data/chartDataManager.js +46 -37
- package/dist/engine/data/chartDataManager.js.map +1 -1
- package/dist/engine/data/incrementalLoadHint.d.ts +2 -4
- package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
- package/dist/engine/data/incrementalLoadHint.js +5 -2
- package/dist/engine/data/incrementalLoadHint.js.map +1 -1
- package/dist/engine/data/scrollCompensator.d.ts +3 -13
- package/dist/engine/data/scrollCompensator.d.ts.map +1 -1
- package/dist/engine/data/scrollCompensator.js +13 -14
- package/dist/engine/data/scrollCompensator.js.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.d.ts +16 -17
- package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.js +37 -37
- package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
- package/dist/engine/layout/chartPaneLayout.d.ts +11 -7
- package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
- package/dist/engine/layout/chartPaneLayout.js +58 -16
- package/dist/engine/layout/chartPaneLayout.js.map +1 -1
- package/dist/engine/paneRenderer.d.ts +2 -0
- package/dist/engine/paneRenderer.d.ts.map +1 -1
- package/dist/engine/paneRenderer.js +15 -5
- package/dist/engine/paneRenderer.js.map +1 -1
- package/dist/engine/render/chartRenderer.d.ts +28 -22
- package/dist/engine/render/chartRenderer.d.ts.map +1 -1
- package/dist/engine/render/chartRenderer.js +117 -69
- package/dist/engine/render/chartRenderer.js.map +1 -1
- package/dist/engine/render/layers/leftYAxisLayer.d.ts +9 -2
- package/dist/engine/render/layers/leftYAxisLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/leftYAxisLayer.js +11 -2
- package/dist/engine/render/layers/leftYAxisLayer.js.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts +6 -4
- package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.js +6 -9
- package/dist/engine/render/layers/mainIndicatorLegendLayer.js.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts +2 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts.map +1 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js +19 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js.map +1 -0
- package/dist/engine/render/layers/yAxisLayer.d.ts +9 -2
- package/dist/engine/render/layers/yAxisLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/yAxisLayer.js +11 -2
- package/dist/engine/render/layers/yAxisLayer.js.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts +7 -3
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +234 -330
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts +72 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts.map +1 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js +177 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js.map +1 -0
- package/dist/engine/renderers/leftYAxis.d.ts +15 -2
- package/dist/engine/renderers/leftYAxis.d.ts.map +1 -1
- package/dist/engine/renderers/leftYAxis.js +56 -16
- package/dist/engine/renderers/leftYAxis.js.map +1 -1
- package/dist/engine/renderers/yAxis.d.ts +16 -6
- package/dist/engine/renderers/yAxis.d.ts.map +1 -1
- package/dist/engine/renderers/yAxis.js +49 -16
- package/dist/engine/renderers/yAxis.js.map +1 -1
- package/dist/engine/state/viewportState.d.ts +4 -1
- package/dist/engine/state/viewportState.d.ts.map +1 -1
- package/dist/engine/state/viewportState.js +40 -8
- package/dist/engine/state/viewportState.js.map +1 -1
- package/dist/engine/subPaneManager.d.ts +5 -3
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +3 -2
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/engine/utils/chartZoomController.d.ts +9 -7
- package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
- package/dist/engine/utils/chartZoomController.js +13 -12
- package/dist/engine/utils/chartZoomController.js.map +1 -1
- package/dist/engine/viewport/chartViewportManager.d.ts +5 -16
- package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -1
- package/dist/engine/viewport/chartViewportManager.js +7 -48
- package/dist/engine/viewport/chartViewportManager.js.map +1 -1
- package/dist/engine/viewport/viewport.d.ts +14 -0
- package/dist/engine/viewport/viewport.d.ts.map +1 -1
- package/dist/engine/viewport/viewport.js +10 -0
- package/dist/engine/viewport/viewport.js.map +1 -1
- package/dist/foundation/plugin/rendererPluginManager.test.js +15 -0
- package/dist/foundation/plugin/rendererPluginManager.test.js.map +1 -1
- package/dist/foundation/plugin/types.d.ts +3 -0
- package/dist/foundation/plugin/types.d.ts.map +1 -1
- package/dist/foundation/plugin/types.js.map +1 -1
- package/dist/foundation/reactivity/frameTransaction.d.ts +11 -0
- package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -1
- package/dist/foundation/reactivity/frameTransaction.js +17 -2
- package/dist/foundation/reactivity/frameTransaction.js.map +1 -1
- package/dist/rendering/render/Renderer.d.ts +58 -74
- package/dist/rendering/render/Renderer.d.ts.map +1 -1
- package/dist/rendering/render/Renderer.js +8 -18
- package/dist/rendering/render/Renderer.js.map +1 -1
- package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -1
- package/dist/rendering/render/createWebGPURenderer.js +30 -17
- package/dist/rendering/render/createWebGPURenderer.js.map +1 -1
- package/dist/rendering/render/createWebGPUSurfaceBackend.js +3 -3
- package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -1
- package/dist/rendering/render/physicalLine.d.ts +4 -0
- package/dist/rendering/render/physicalLine.d.ts.map +1 -0
- package/dist/rendering/render/physicalLine.js +37 -0
- package/dist/rendering/render/physicalLine.js.map +1 -0
- package/dist/rendering/render/physicalRegion.d.ts +14 -0
- package/dist/rendering/render/physicalRegion.d.ts.map +1 -0
- package/dist/rendering/render/physicalRegion.js +15 -0
- package/dist/rendering/render/physicalRegion.js.map +1 -0
- package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
- package/dist/rendering/scene/createLayerFromPlugin.js +8 -3
- package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
- package/dist/rendering/scene/createScene.d.ts.map +1 -1
- package/dist/rendering/scene/createScene.js +4 -3
- package/dist/rendering/scene/createScene.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/stateKernel.test.ts +1 -0
- package/src/controllers/createChartController.ts +9 -8
- package/src/controllers/index.ts +11 -0
- package/src/controllers/types.ts +7 -0
- package/src/data/binance.ts +1 -1
- package/src/engine/__tests__/chart.dpr.test.ts +3 -2
- package/src/engine/__tests__/paneRenderer.resize.test.ts +52 -54
- package/src/engine/__tests__/subPaneManager.test.ts +5 -3
- package/src/engine/chart.ts +76 -109
- package/src/engine/chartTypes.ts +5 -0
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +39 -2
- package/src/engine/controller/interaction.ts +18 -10
- package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +37 -17
- package/src/engine/data/__tests__/scrollCompensator.geometry.test.ts +87 -75
- package/src/engine/data/chartDataManager.ts +54 -59
- package/src/engine/data/incrementalLoadHint.ts +8 -7
- package/src/engine/data/scrollCompensator.ts +16 -24
- package/src/engine/indicators/__tests__/chartIndicatorManager.test.ts +18 -26
- package/src/engine/indicators/chartIndicatorManager.ts +62 -62
- package/src/engine/layout/chartPaneLayout.ts +73 -21
- package/src/engine/paneRenderer.ts +17 -10
- package/src/engine/render/chartRenderer.ts +187 -129
- package/src/engine/render/layers/leftYAxisLayer.ts +29 -7
- package/src/engine/render/layers/mainIndicatorLegendLayer.test.ts +26 -0
- package/src/engine/render/layers/mainIndicatorLegendLayer.ts +13 -10
- package/src/engine/render/layers/yAxisLayer.ts +29 -7
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +270 -403
- package/src/engine/renderers/Indicator/mainIndicatorLegendContext.ts +282 -0
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +73 -2
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +10 -6
- package/src/engine/renderers/leftYAxis.ts +60 -18
- package/src/engine/renderers/yAxis.ts +57 -22
- package/src/engine/state/viewportState.ts +44 -8
- package/src/engine/subPaneManager.ts +8 -5
- package/src/engine/utils/chartZoomController.ts +22 -19
- package/src/engine/viewport/__tests__/visibleRange.clamp.test.ts +50 -0
- package/src/engine/viewport/chartViewportManager.ts +9 -60
- package/src/engine/viewport/viewport.ts +14 -0
- package/src/foundation/plugin/rendererPluginManager.test.ts +18 -0
- package/src/foundation/plugin/types.ts +3 -0
- package/src/foundation/reactivity/frameTransaction.ts +20 -6
- package/src/rendering/render/Renderer.ts +69 -85
- package/src/rendering/render/__tests__/physicalLine.test.ts +51 -0
- package/src/rendering/render/__tests__/physicalRegion.test.ts +23 -0
- package/src/rendering/render/__tests__/webgpuRenderer.test.ts +31 -1
- package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +11 -0
- package/src/rendering/render/createWebGPURenderer.ts +30 -17
- package/src/rendering/render/createWebGPUSurfaceBackend.ts +3 -3
- package/src/rendering/render/physicalLine.ts +45 -0
- package/src/rendering/render/physicalRegion.ts +32 -0
- package/src/rendering/scene/createLayerFromPlugin.ts +8 -3
- package/src/rendering/scene/createScene.ts +4 -3
- package/src/version.ts +1 -1
|
@@ -1,60 +1,95 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* scene layers (candles, volume, indicators, drawings) call into.
|
|
2
|
+
* 渲染器接口 — Scene 各层(蜡烛、指标、drawing 等)通过它画图。
|
|
4
3
|
*
|
|
5
|
-
* WebGL
|
|
6
|
-
* will implement the same surface. Call sites only use sceneRenderer
|
|
7
|
-
* (fail-closed boolean → Canvas2D), never dual-path surfaces.
|
|
4
|
+
* WebGL 和 WebGPU 实现同一套接口。调用方只走 sceneRenderer(返回 false 时 fail-closed 降级 Canvas2D),不走双路径。
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
* - `dispatchCompute` exists only because WebGPU has compute shaders;
|
|
16
|
-
* the WebGL implementation throws on call so callers can feature-detect
|
|
17
|
-
* via `caps.compute`. Volume Profile binning, M4 downsampling, footprint
|
|
18
|
-
* aggregation will use it on the WebGPU path.
|
|
19
|
-
* - `setUniforms` is intentionally schemaless on the interface — the
|
|
20
|
-
* uniform layout is owned by each shader/program. Callers pass a
|
|
21
|
-
* structured object that the backend translates into uniform writes.
|
|
6
|
+
* 设计说明:
|
|
7
|
+
* - drawInstances 是主力:一份 unit geometry 配 instance buffer 渲染 N 份。
|
|
8
|
+
* 蜡烛、成交量柱、footprint 格子、heatmap 磁贴都归约为 instanced 图元。
|
|
9
|
+
* - drawLines 画 MA-style 折线、drawing、坐标轴。
|
|
10
|
+
* - dispatchCompute 只有 WebGPU 有;WebGL 实现会抛异常,调用方先查 caps.compute。
|
|
11
|
+
* - uniform 布局由各 shader/pipeline 自行定义,接口不做 schema 约束。
|
|
22
12
|
*/
|
|
23
13
|
|
|
24
14
|
import type { SurfaceBackend, SurfaceRegion } from './SurfaceBackend'
|
|
25
15
|
|
|
26
|
-
/**
|
|
16
|
+
/** 后端上报的能力位,调用方据此分支 */
|
|
27
17
|
export type RendererCapabilities = {
|
|
28
|
-
/** WebGPU compute
|
|
18
|
+
/** WebGPU compute shader 是否可用;WebGL 返回 false */
|
|
29
19
|
compute: boolean
|
|
30
|
-
/**
|
|
20
|
+
/** storage buffer 是否可用 */
|
|
31
21
|
storageBuffer: boolean
|
|
32
|
-
/**
|
|
22
|
+
/** 单次 drawInstances 上限(引擎强加) */
|
|
33
23
|
maxInstances: number
|
|
34
|
-
/**
|
|
24
|
+
/** 名称,如 "webgl2" 或 "webgpu" */
|
|
35
25
|
name: string
|
|
36
26
|
}
|
|
37
27
|
|
|
38
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* 各后端(WebGL/WebGPU/Canvas2D)都实现的绘制接口。
|
|
30
|
+
* 后端持有 GPU 资源,调用方通过不透明 handle 操作。
|
|
31
|
+
*/
|
|
32
|
+
export interface Renderer {
|
|
33
|
+
/** 底层 surface,用于 region 绑定和最终合成 */
|
|
34
|
+
readonly surface: SurfaceBackend
|
|
35
|
+
|
|
36
|
+
/** 能力位标志 */
|
|
37
|
+
readonly caps: RendererCapabilities
|
|
38
|
+
|
|
39
|
+
// --- 资源生命周期 ---
|
|
40
|
+
|
|
41
|
+
createBuffer(usage: BufferUsage, sizeBytes: number): BufferHandle
|
|
42
|
+
writeBuffer(handle: BufferHandle, data: ArrayBufferView, offsetBytes?: number): void
|
|
43
|
+
destroyBuffer(handle: BufferHandle): void
|
|
44
|
+
|
|
45
|
+
/** 编译 shader pipeline。descriptor 格式后端自定,适配层通常提供工厂函数(如 createCandlePipeline) */
|
|
46
|
+
createPipeline(descriptor: unknown): PipelineHandle
|
|
47
|
+
destroyPipeline(handle: PipelineHandle): void
|
|
48
|
+
|
|
49
|
+
/** WebGPU only — WebGL 会抛异常 */
|
|
50
|
+
createComputePipeline(descriptor: unknown): ComputePipelineHandle
|
|
51
|
+
destroyComputePipeline(handle: ComputePipelineHandle): void
|
|
52
|
+
|
|
53
|
+
// --- 帧 ---
|
|
54
|
+
|
|
55
|
+
beginFrame(region: SurfaceRegion): void
|
|
56
|
+
/**
|
|
57
|
+
* 返回 true 表示本批已成功提交 GPU(或 instanceCount<=0 无需绘制)。
|
|
58
|
+
* 返回 false 表示未画上(无 surface / pipeline 不匹配 / 资源缺失)——调用方应 fail-closed 走 2D。
|
|
59
|
+
*/
|
|
60
|
+
drawInstances(params: DrawInstancesParams): boolean
|
|
61
|
+
/**
|
|
62
|
+
* 返回 true 表示本批折线/填充已成功提交 GPU。
|
|
63
|
+
* 返回 false 表示未画上——调用方应 fail-closed 走 2D。
|
|
64
|
+
*/
|
|
65
|
+
drawLines(params: DrawLinesParams): boolean
|
|
66
|
+
/** WebGPU only — WebGL 会抛异常,调用方先查 caps */
|
|
67
|
+
dispatchCompute(params: DispatchComputeParams): void
|
|
68
|
+
endFrame(): void
|
|
69
|
+
|
|
70
|
+
dispose(): void
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 不透明 buffer handle,后端持有 GPU 资源 */
|
|
39
74
|
export type BufferHandle = { readonly __brand: 'BufferHandle' }
|
|
40
|
-
/**
|
|
75
|
+
/** 不透明 pipeline handle */
|
|
41
76
|
export type PipelineHandle = { readonly __brand: 'PipelineHandle' }
|
|
42
|
-
/**
|
|
77
|
+
/** WebGPU compute pipeline handle */
|
|
43
78
|
export type ComputePipelineHandle = { readonly __brand: 'ComputePipelineHandle' }
|
|
44
79
|
|
|
45
80
|
export type BufferUsage = 'vertex' | 'instance' | 'index' | 'uniform' | 'storage'
|
|
46
81
|
|
|
47
82
|
export type DrawInstancesParams = {
|
|
48
83
|
pipeline: PipelineHandle
|
|
49
|
-
/**
|
|
84
|
+
/** 所有 instance 共享的 unit geometry(如一个矩形) */
|
|
50
85
|
vertices: BufferHandle
|
|
51
|
-
/**
|
|
86
|
+
/** 每个 instance 的属性 buffer(如一行 K 线数据) */
|
|
52
87
|
instances: BufferHandle
|
|
53
|
-
/**
|
|
88
|
+
/** 绘制多少个 instance */
|
|
54
89
|
instanceCount: number
|
|
55
|
-
/**
|
|
90
|
+
/** 每个 instance 的顶点数(quad 用 4,triangle 用 6) */
|
|
56
91
|
vertexCount: number
|
|
57
|
-
/**
|
|
92
|
+
/** uniform 块,后端自行解译 */
|
|
58
93
|
uniforms?: Record<string, unknown>
|
|
59
94
|
}
|
|
60
95
|
|
|
@@ -72,7 +107,7 @@ export type DrawLinesParams = {
|
|
|
72
107
|
*/
|
|
73
108
|
vertices?: BufferHandle
|
|
74
109
|
vertexCount?: number
|
|
75
|
-
/**
|
|
110
|
+
/** strip 连续折线 / list 独立线段 */
|
|
76
111
|
topology?: 'strip' | 'list'
|
|
77
112
|
/** 多色折线批量(推荐 MA 等多周期) */
|
|
78
113
|
strips?: ReadonlyArray<DrawLineStrip>
|
|
@@ -81,60 +116,9 @@ export type DrawLinesParams = {
|
|
|
81
116
|
|
|
82
117
|
export type DispatchComputeParams = {
|
|
83
118
|
pipeline: ComputePipelineHandle
|
|
84
|
-
/**
|
|
119
|
+
/** 各轴 workgroup 数 */
|
|
85
120
|
workgroups: [number, number?, number?]
|
|
86
|
-
/**
|
|
121
|
+
/** 输入/输出 buffer 绑定,key 为 binding index */
|
|
87
122
|
bindings: Record<number, BufferHandle>
|
|
88
123
|
uniforms?: Record<string, unknown>
|
|
89
124
|
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* The drawing-primitives interface every backend implements.
|
|
93
|
-
*
|
|
94
|
-
* Backends own the GPU resources; callers receive opaque handles
|
|
95
|
-
* (`BufferHandle`, `PipelineHandle`) and pass them back for draws.
|
|
96
|
-
*/
|
|
97
|
-
export interface Renderer {
|
|
98
|
-
/** Underlying surface (for region binding and final composite). */
|
|
99
|
-
readonly surface: SurfaceBackend
|
|
100
|
-
|
|
101
|
-
/** Reported feature flags — caller branches on `caps.compute` etc. */
|
|
102
|
-
readonly caps: RendererCapabilities
|
|
103
|
-
|
|
104
|
-
// --- resource lifecycle ---
|
|
105
|
-
|
|
106
|
-
createBuffer(usage: BufferUsage, sizeBytes: number): BufferHandle
|
|
107
|
-
writeBuffer(handle: BufferHandle, data: ArrayBufferView, offsetBytes?: number): void
|
|
108
|
-
destroyBuffer(handle: BufferHandle): void
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Compile a shader pipeline. The exact pipeline descriptor is backend-specific;
|
|
112
|
-
* adapters typically expose factory helpers (e.g. `createCandlePipeline`) that
|
|
113
|
-
* internally call this with the right vertex/fragment sources.
|
|
114
|
-
*/
|
|
115
|
-
createPipeline(descriptor: unknown): PipelineHandle
|
|
116
|
-
destroyPipeline(handle: PipelineHandle): void
|
|
117
|
-
|
|
118
|
-
/** WebGPU only — WebGL implementations MUST throw with a clear message. */
|
|
119
|
-
createComputePipeline(descriptor: unknown): ComputePipelineHandle
|
|
120
|
-
destroyComputePipeline(handle: ComputePipelineHandle): void
|
|
121
|
-
|
|
122
|
-
// --- frame ---
|
|
123
|
-
|
|
124
|
-
beginFrame(region: SurfaceRegion): void
|
|
125
|
-
/**
|
|
126
|
-
* 返回 true 表示本批已成功提交 GPU(或 instanceCount<=0 无需绘制)。
|
|
127
|
-
* 返回 false 表示未画上(无 surface / pipeline 不匹配 / 资源缺失)——调用方应 fail-closed 走 2D。
|
|
128
|
-
*/
|
|
129
|
-
drawInstances(params: DrawInstancesParams): boolean
|
|
130
|
-
/**
|
|
131
|
-
* 返回 true 表示本批折线/填充已成功提交 GPU。
|
|
132
|
-
* 返回 false 表示未画上——调用方应 fail-closed 走 2D。
|
|
133
|
-
*/
|
|
134
|
-
drawLines(params: DrawLinesParams): boolean
|
|
135
|
-
/** WebGPU only — WebGL implementations MUST throw. Caller checks caps first. */
|
|
136
|
-
dispatchCompute(params: DispatchComputeParams): void
|
|
137
|
-
endFrame(): void
|
|
138
|
-
|
|
139
|
-
dispose(): void
|
|
140
|
-
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { prepareLineStripForPhysicalPixels } from '../physicalLine'
|
|
4
|
+
|
|
5
|
+
describe('prepareLineStripForPhysicalPixels', () => {
|
|
6
|
+
it('snaps a horizontal one-pixel line to the physical pixel center', () => {
|
|
7
|
+
const strip = prepareLineStripForPhysicalPixels(
|
|
8
|
+
{
|
|
9
|
+
points: [
|
|
10
|
+
{ x: 0.2, y: 5.1 },
|
|
11
|
+
{ x: 10.7, y: 5.1 },
|
|
12
|
+
],
|
|
13
|
+
color: '#f00',
|
|
14
|
+
width: 1,
|
|
15
|
+
},
|
|
16
|
+
1.25,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
expect(strip).toEqual({
|
|
20
|
+
points: [
|
|
21
|
+
{ x: 0, y: 5.2 },
|
|
22
|
+
{ x: 10.4, y: 5.2 },
|
|
23
|
+
],
|
|
24
|
+
color: '#f00',
|
|
25
|
+
width: 0.8,
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('preserves diagonal vertices while quantizing the physical line width', () => {
|
|
30
|
+
const strip = prepareLineStripForPhysicalPixels(
|
|
31
|
+
{
|
|
32
|
+
points: [
|
|
33
|
+
{ x: 0.2, y: 5.1 },
|
|
34
|
+
{ x: 10.7, y: 8.4 },
|
|
35
|
+
],
|
|
36
|
+
color: '#0f0',
|
|
37
|
+
width: 1,
|
|
38
|
+
},
|
|
39
|
+
1.25,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
expect(strip).toEqual({
|
|
43
|
+
points: [
|
|
44
|
+
{ x: 0.2, y: 5.1 },
|
|
45
|
+
{ x: 10.7, y: 8.4 },
|
|
46
|
+
],
|
|
47
|
+
color: '#0f0',
|
|
48
|
+
width: 0.8,
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { toPhysicalRegion } from '../physicalRegion'
|
|
4
|
+
|
|
5
|
+
describe('toPhysicalRegion', () => {
|
|
6
|
+
it('keeps adjacent fractional-DPR regions contiguous', () => {
|
|
7
|
+
const upper = toPhysicalRegion({ x: 0, y: 0, width: 320, height: 101.2, dpr: 1.25 })
|
|
8
|
+
const lower = toPhysicalRegion({ x: 0, y: 101.2, width: 320, height: 98.8, dpr: 1.25 })
|
|
9
|
+
|
|
10
|
+
expect(upper).toEqual({ x: 0, y: 0, width: 400, height: 127 })
|
|
11
|
+
expect(lower).toEqual({ x: 0, y: 127, width: 400, height: 123 })
|
|
12
|
+
expect(upper.y + upper.height).toBe(lower.y)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('clips a physical region to the backing canvas', () => {
|
|
16
|
+
expect(
|
|
17
|
+
toPhysicalRegion(
|
|
18
|
+
{ x: -0.4, y: 10, width: 100.8, height: 20, dpr: 1.25 },
|
|
19
|
+
{ width: 125, height: 100 },
|
|
20
|
+
),
|
|
21
|
+
).toEqual({ x: 0, y: 13, width: 125, height: 25 })
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -159,6 +159,15 @@ describe('createWebGPURenderer', () => {
|
|
|
159
159
|
expect(fake.passes[0]?.setScissorRect).toHaveBeenCalledWith(20, 10, 160, 80)
|
|
160
160
|
expect(fake.passes[0]?.draw).toHaveBeenCalledWith(6, 3)
|
|
161
161
|
expect(fake.queue.submit).toHaveBeenCalledOnce()
|
|
162
|
+
|
|
163
|
+
const uniformWrite = fake.queue.writeBuffer.mock.calls.find((call) => call[4] === 32)
|
|
164
|
+
expect(uniformWrite).toBeDefined()
|
|
165
|
+
expect(
|
|
166
|
+
Array.from(new Float32Array(uniformWrite![2] as ArrayBuffer, uniformWrite![3], 8)),
|
|
167
|
+
).toEqual([160, 80, 2, 4, 1, 0, 0, 1])
|
|
168
|
+
expect(
|
|
169
|
+
(fake.pipelineDescriptors[0]?.vertex.module as { code: string }).code,
|
|
170
|
+
).toContain('round((rect.x - uniforms.scrollLeft) * uniforms.dpr)')
|
|
162
171
|
})
|
|
163
172
|
|
|
164
173
|
it('records multiple draws and submits once on endFrame', async () => {
|
|
@@ -189,6 +198,27 @@ describe('createWebGPURenderer', () => {
|
|
|
189
198
|
expect(fake.queue.submit).toHaveBeenCalledTimes(1)
|
|
190
199
|
})
|
|
191
200
|
|
|
201
|
+
it('expands a logical one-pixel line to its physical DPR width', async () => {
|
|
202
|
+
const fake = makeWebGPU()
|
|
203
|
+
const renderer = await createWebGPURenderer({
|
|
204
|
+
gpu: fake.gpu as unknown as GPU,
|
|
205
|
+
canvas: fake.canvas,
|
|
206
|
+
})
|
|
207
|
+
renderer.surface.resize(100, 100, 2)
|
|
208
|
+
renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 2 })
|
|
209
|
+
const pipeline = renderer.createPipeline({ type: 'line' })
|
|
210
|
+
|
|
211
|
+
expect(
|
|
212
|
+
renderer.drawLines({
|
|
213
|
+
pipeline,
|
|
214
|
+
strips: [{ points: [{ x: 0, y: 10 }, { x: 100, y: 10 }], color: '#f00', width: 1 }],
|
|
215
|
+
}),
|
|
216
|
+
).toBe(true)
|
|
217
|
+
renderer.endFrame()
|
|
218
|
+
|
|
219
|
+
expect(fake.pipelineDescriptors[0]?.primitive?.topology).toBe('triangle-list')
|
|
220
|
+
})
|
|
221
|
+
|
|
192
222
|
it('preserves earlier rectangle batches within the same frame', async () => {
|
|
193
223
|
const fake = makeWebGPU()
|
|
194
224
|
const renderer = await createWebGPURenderer({
|
|
@@ -262,7 +292,7 @@ describe('createWebGPURenderer', () => {
|
|
|
262
292
|
const uniformWrites = fake.queue.writeBuffer.mock.calls.filter((call) => call[4] === 32)
|
|
263
293
|
expect(uniformWrites).toHaveLength(2)
|
|
264
294
|
for (const call of uniformWrites) {
|
|
265
|
-
expect(new Float32Array(call[2] as ArrayBuffer)[
|
|
295
|
+
expect(new Float32Array(call[2] as ArrayBuffer)[3]).toBe(12)
|
|
266
296
|
}
|
|
267
297
|
expect(fake.queue.submit).toHaveBeenCalledOnce()
|
|
268
298
|
})
|
|
@@ -51,6 +51,17 @@ describe('createWebGPUSurfaceBackend', () => {
|
|
|
51
51
|
expect(canvas.style.height).toBe('180px')
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
+
it('derives CSS size from the rounded backing buffer', () => {
|
|
55
|
+
const { canvas, surface } = makeSurface()
|
|
56
|
+
|
|
57
|
+
surface.resize(101.2, 80.4, 1.25)
|
|
58
|
+
|
|
59
|
+
expect(canvas.width).toBe(127)
|
|
60
|
+
expect(canvas.height).toBe(101)
|
|
61
|
+
expect(canvas.style.width).toBe('101.6px')
|
|
62
|
+
expect(canvas.style.height).toBe('80.8px')
|
|
63
|
+
})
|
|
64
|
+
|
|
54
65
|
it('compositeTo is a no-op under hybrid DOM (M2)', () => {
|
|
55
66
|
const { surface } = makeSurface()
|
|
56
67
|
const target = {
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
} from './Renderer'
|
|
12
12
|
import { createWebGPUSurfaceBackend, type WebGPUSurfaceBackend } from './createWebGPUSurfaceBackend'
|
|
13
13
|
import { createFrameMetrics } from './frameMetrics'
|
|
14
|
+
import { prepareLineStripForPhysicalPixels } from './physicalLine'
|
|
15
|
+
import { toPhysicalRegion } from './physicalRegion'
|
|
14
16
|
import { createWebGPUResourceTable } from './webgpuResourceTable'
|
|
15
17
|
|
|
16
18
|
type PipelineType = 'candle' | 'line' | 'fill'
|
|
@@ -42,8 +44,8 @@ const GPU_TEXTURE_RENDER_ATTACHMENT = globalThis.GPUTextureUsage?.RENDER_ATTACHM
|
|
|
42
44
|
const RECT_SHADER = `
|
|
43
45
|
struct Uniforms {
|
|
44
46
|
resolution: vec2f,
|
|
47
|
+
dpr: f32,
|
|
45
48
|
scrollLeft: f32,
|
|
46
|
-
padding: f32,
|
|
47
49
|
color: vec4f,
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -56,7 +58,14 @@ fn vertexMain(@builtin(vertex_index) vertexIndex: u32, @location(0) rect: vec4f)
|
|
|
56
58
|
vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0)
|
|
57
59
|
);
|
|
58
60
|
let unit = corners[vertexIndex];
|
|
59
|
-
let
|
|
61
|
+
let left = round((rect.x - uniforms.scrollLeft) * uniforms.dpr);
|
|
62
|
+
let top = round(rect.y * uniforms.dpr);
|
|
63
|
+
let right = round((rect.x + rect.z - uniforms.scrollLeft) * uniforms.dpr);
|
|
64
|
+
let bottom = round((rect.y + rect.w) * uniforms.dpr);
|
|
65
|
+
let position = vec2f(
|
|
66
|
+
left + unit.x * max(1.0, right - left),
|
|
67
|
+
top + unit.y * max(1.0, bottom - top),
|
|
68
|
+
);
|
|
60
69
|
let clip = vec2f(position.x / uniforms.resolution.x * 2.0 - 1.0, 1.0 - position.y / uniforms.resolution.y * 2.0);
|
|
61
70
|
return vec4f(clip, 0.0, 1.0);
|
|
62
71
|
}
|
|
@@ -70,8 +79,8 @@ fn fragmentMain() -> @location(0) vec4f {
|
|
|
70
79
|
const LINE_SHADER = `
|
|
71
80
|
struct Uniforms {
|
|
72
81
|
resolution: vec2f,
|
|
82
|
+
dpr: f32,
|
|
73
83
|
scrollLeft: f32,
|
|
74
|
-
padding: f32,
|
|
75
84
|
color: vec4f,
|
|
76
85
|
}
|
|
77
86
|
|
|
@@ -79,7 +88,10 @@ struct Uniforms {
|
|
|
79
88
|
|
|
80
89
|
@vertex
|
|
81
90
|
fn vertexMain(@location(0) point: vec2f) -> @builtin(position) vec4f {
|
|
82
|
-
let position = vec2f(
|
|
91
|
+
let position = vec2f(
|
|
92
|
+
(point.x - uniforms.scrollLeft) * uniforms.dpr,
|
|
93
|
+
point.y * uniforms.dpr,
|
|
94
|
+
);
|
|
83
95
|
let clip = vec2f(position.x / uniforms.resolution.x * 2.0 - 1.0, 1.0 - position.y / uniforms.resolution.y * 2.0);
|
|
84
96
|
return vec4f(clip, 0.0, 1.0);
|
|
85
97
|
}
|
|
@@ -368,10 +380,10 @@ export async function createWebGPURenderer(
|
|
|
368
380
|
const color = parseColor(colorValue ?? '#000000')
|
|
369
381
|
if (!color) return null
|
|
370
382
|
const values = new Float32Array([
|
|
371
|
-
region.width,
|
|
372
|
-
region.height,
|
|
383
|
+
Math.round(region.width * region.dpr),
|
|
384
|
+
Math.round(region.height * region.dpr),
|
|
385
|
+
region.dpr,
|
|
373
386
|
scrollLeft,
|
|
374
|
-
0,
|
|
375
387
|
color[0],
|
|
376
388
|
color[1],
|
|
377
389
|
color[2],
|
|
@@ -406,10 +418,10 @@ export async function createWebGPURenderer(
|
|
|
406
418
|
},
|
|
407
419
|
],
|
|
408
420
|
})
|
|
409
|
-
const x
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
421
|
+
const { x, y, width, height } = toPhysicalRegion(region, {
|
|
422
|
+
width: canvas.width,
|
|
423
|
+
height: canvas.height,
|
|
424
|
+
})
|
|
413
425
|
if (width <= 0 || height <= 0) {
|
|
414
426
|
pass.end()
|
|
415
427
|
return null
|
|
@@ -456,10 +468,10 @@ export async function createWebGPURenderer(
|
|
|
456
468
|
const region = draws[0]!.region
|
|
457
469
|
rawSurface.bindRegion(region)
|
|
458
470
|
|
|
459
|
-
const x
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
471
|
+
const { x, y, width, height } = toPhysicalRegion(region, {
|
|
472
|
+
width: canvas.width,
|
|
473
|
+
height: canvas.height,
|
|
474
|
+
})
|
|
463
475
|
if (width > 0 && height > 0) {
|
|
464
476
|
pass.setViewport(x, y, width, height, 0, 1)
|
|
465
477
|
pass.setScissorRect(x, y, width, height)
|
|
@@ -608,8 +620,9 @@ export async function createWebGPURenderer(
|
|
|
608
620
|
const scrollLeft = (params.uniforms?.scrollLeft as number) ?? 0
|
|
609
621
|
for (const strip of params.strips) {
|
|
610
622
|
if (strip.points.length < 2) return false
|
|
611
|
-
const
|
|
612
|
-
const
|
|
623
|
+
const physicalStrip = prepareLineStripForPhysicalPixels(strip, currentRegion.dpr)
|
|
624
|
+
const wide = Math.round((physicalStrip.width ?? 1) * currentRegion.dpr) > 1
|
|
625
|
+
const values = wide ? buildWideLine(physicalStrip) : linePoints(physicalStrip)
|
|
613
626
|
if (!values) return false
|
|
614
627
|
// 帧内序号作 key:同顺序跨帧复用;revision 未变则不 upload
|
|
615
628
|
const key = `strip/${stripSeq++}`
|
|
@@ -45,10 +45,10 @@ export function createWebGPUSurfaceBackend(
|
|
|
45
45
|
const height = Math.max(1, Math.round(heightLogical * dpr))
|
|
46
46
|
if (canvas.width !== width) canvas.width = width
|
|
47
47
|
if (canvas.height !== height) canvas.height = height
|
|
48
|
-
//
|
|
48
|
+
// CSS 尺寸由物理 buffer 反算,避免浏览器二次缩放。
|
|
49
49
|
if (canvas.style) {
|
|
50
|
-
canvas.style.width = `${
|
|
51
|
-
canvas.style.height = `${
|
|
50
|
+
canvas.style.width = `${width / dpr}px`
|
|
51
|
+
canvas.style.height = `${height / dpr}px`
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
bindRegion(region: SurfaceRegion): boolean {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DrawLineStrip } from './Renderer'
|
|
2
|
+
|
|
3
|
+
function physicalLineWidth(width: number, dpr: number): number {
|
|
4
|
+
return Math.max(1, Math.round(width * dpr))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function alignedCenter(value: number, widthPx: number, dpr: number): number {
|
|
8
|
+
const base = Math.floor(value * dpr)
|
|
9
|
+
return (base + (widthPx % 2 === 0 ? 0 : 0.5)) / dpr
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function alignedEdge(value: number, dpr: number): number {
|
|
13
|
+
return Math.round(value * dpr) / dpr
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 仅对轴向线吸附物理像素;斜线保持原始顶点供 MSAA 平滑。 */
|
|
17
|
+
export function prepareLineStripForPhysicalPixels(
|
|
18
|
+
strip: DrawLineStrip,
|
|
19
|
+
dpr: number,
|
|
20
|
+
): DrawLineStrip {
|
|
21
|
+
const widthPx = physicalLineWidth(strip.width ?? 1, dpr)
|
|
22
|
+
const width = widthPx / dpr
|
|
23
|
+
const first = strip.points[0]
|
|
24
|
+
if (!first) return { ...strip, width }
|
|
25
|
+
|
|
26
|
+
const horizontal = strip.points.every((point) => point.y === first.y)
|
|
27
|
+
const vertical = strip.points.every((point) => point.x === first.x)
|
|
28
|
+
if (!horizontal && !vertical) return { ...strip, width }
|
|
29
|
+
|
|
30
|
+
if (horizontal) {
|
|
31
|
+
const y = alignedCenter(first.y, widthPx, dpr)
|
|
32
|
+
return {
|
|
33
|
+
...strip,
|
|
34
|
+
width,
|
|
35
|
+
points: strip.points.map((point) => ({ x: alignedEdge(point.x, dpr), y })),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const x = alignedCenter(first.x, widthPx, dpr)
|
|
40
|
+
return {
|
|
41
|
+
...strip,
|
|
42
|
+
width,
|
|
43
|
+
points: strip.points.map((point) => ({ x, y: alignedEdge(point.y, dpr) })),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SurfaceRegion } from './SurfaceBackend'
|
|
2
|
+
|
|
3
|
+
export type PhysicalRegion = {
|
|
4
|
+
x: number
|
|
5
|
+
y: number
|
|
6
|
+
width: number
|
|
7
|
+
height: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type PhysicalBounds = {
|
|
11
|
+
width: number
|
|
12
|
+
height: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 将逻辑 region 的两端分别对齐到物理像素,避免相邻 pane 出现缝隙。 */
|
|
16
|
+
export function toPhysicalRegion(
|
|
17
|
+
region: SurfaceRegion,
|
|
18
|
+
bounds?: PhysicalBounds,
|
|
19
|
+
): PhysicalRegion {
|
|
20
|
+
const left = Math.round(region.x * region.dpr)
|
|
21
|
+
const top = Math.round(region.y * region.dpr)
|
|
22
|
+
const right = Math.round((region.x + region.width) * region.dpr)
|
|
23
|
+
const bottom = Math.round((region.y + region.height) * region.dpr)
|
|
24
|
+
const maxWidth = bounds ? Math.max(0, bounds.width) : Infinity
|
|
25
|
+
const maxHeight = bounds ? Math.max(0, bounds.height) : Infinity
|
|
26
|
+
const x = Math.max(0, Math.min(left, maxWidth))
|
|
27
|
+
const y = Math.max(0, Math.min(top, maxHeight))
|
|
28
|
+
const clippedRight = Math.max(x, Math.min(right, maxWidth))
|
|
29
|
+
const clippedBottom = Math.max(y, Math.min(bottom, maxHeight))
|
|
30
|
+
|
|
31
|
+
return { x, y, width: clippedRight - x, height: clippedBottom - y }
|
|
32
|
+
}
|
|
@@ -23,18 +23,23 @@ export function createLayerFromPlugin(
|
|
|
23
23
|
set visible(v: boolean) {
|
|
24
24
|
visible = v
|
|
25
25
|
},
|
|
26
|
+
/**
|
|
27
|
+
* @Todo 兼容层:把旧 RendererPlugin.draw 包装成 Layer.paint。
|
|
28
|
+
*/
|
|
26
29
|
paint(ctx: PaintContext) {
|
|
27
30
|
if (!visible) return
|
|
28
|
-
//
|
|
31
|
+
// 子图 layer 只画自己的 pane
|
|
29
32
|
if (paneRole === 'sub' && ctx.paneId !== targetPaneId) return
|
|
33
|
+
// 获取业务层 RenderContext(含数据、几何、2D ctx)
|
|
30
34
|
const context = getContext()
|
|
31
35
|
if (!context) return
|
|
32
|
-
// 注入本帧
|
|
36
|
+
// 注入本帧 sceneRenderer,已迁路径走统一 GPU 画笔,未迁路径仍用 context.ctx
|
|
33
37
|
context.sceneRenderer = ctx.renderer
|
|
34
38
|
try {
|
|
39
|
+
// 调用各个渲染器的绘制能力
|
|
35
40
|
plugin.draw(context)
|
|
36
41
|
} catch (e) {
|
|
37
|
-
//
|
|
42
|
+
// 异常隔离,不中断同 pane 后续 layer
|
|
38
43
|
console.error(`[RendererPlugin] ${plugin.name} draw error:`, e)
|
|
39
44
|
}
|
|
40
45
|
},
|
|
@@ -73,17 +73,18 @@ export function createScene(): Scene {
|
|
|
73
73
|
return true
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** 按 role 和可见性过滤 layer,z 排序后逐层 paint */
|
|
76
77
|
const paintPane = (ctx: PaintContext, roles?: ReadonlyArray<LayerRole>): void => {
|
|
77
78
|
if (disposed) return
|
|
79
|
+
// 选出属于当前 pane 或全局、且可见的 layer
|
|
78
80
|
let candidates = layerList.filter(
|
|
79
81
|
(layer) => (layer.paneRole === ctx.paneRole || layer.paneRole === 'global') && layer.visible,
|
|
80
82
|
)
|
|
83
|
+
// 若传了 roles 参数,进一步按角色过滤(如只画 overlay)
|
|
81
84
|
if (roles) {
|
|
82
85
|
candidates = candidates.filter((layer) => roles.includes(layer.role))
|
|
83
86
|
}
|
|
84
|
-
//
|
|
85
|
-
// by ECMAScript 2019+ to be stable, so equal z values preserve the
|
|
86
|
-
// surviving registration order from the filter pass.
|
|
87
|
+
// 按 z 升序稳定排序,相同时保留注册顺序(ECMAScript 2019+ 保证 sort 稳定)
|
|
87
88
|
candidates.sort((a, b) => a.z - b.z)
|
|
88
89
|
for (const layer of candidates) {
|
|
89
90
|
layer.paint(ctx)
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.9.0-alpha.
|
|
1
|
+
export const VERSION = '0.9.0-alpha.3'
|