pose-ai-core 0.1.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 (61) hide show
  1. package/README.md +348 -0
  2. package/dist/_virtual/_commonjsHelpers.js +5 -0
  3. package/dist/_virtual/_commonjsHelpers.js.map +1 -0
  4. package/dist/_virtual/camera_utils.js +6 -0
  5. package/dist/_virtual/camera_utils.js.map +1 -0
  6. package/dist/_virtual/camera_utils2.js +5 -0
  7. package/dist/_virtual/camera_utils2.js.map +1 -0
  8. package/dist/_virtual/drawing_utils.js +6 -0
  9. package/dist/_virtual/drawing_utils.js.map +1 -0
  10. package/dist/_virtual/drawing_utils2.js +5 -0
  11. package/dist/_virtual/drawing_utils2.js.map +1 -0
  12. package/dist/_virtual/pose.js +6 -0
  13. package/dist/_virtual/pose.js.map +1 -0
  14. package/dist/_virtual/pose2.js +5 -0
  15. package/dist/_virtual/pose2.js.map +1 -0
  16. package/dist/components/AnglePanel/index.css +1 -0
  17. package/dist/components/AnglePanel/index.js +79 -0
  18. package/dist/components/AnglePanel/index.js.map +1 -0
  19. package/dist/components/PoseCanvas/index.js +86 -0
  20. package/dist/components/PoseCanvas/index.js.map +1 -0
  21. package/dist/components/ROMVisualizer/index.css +1 -0
  22. package/dist/components/ROMVisualizer/index.js +208 -0
  23. package/dist/components/ROMVisualizer/index.js.map +1 -0
  24. package/dist/components/SkeletonOverlay/index.css +1 -0
  25. package/dist/components/SkeletonOverlay/index.js +45 -0
  26. package/dist/components/SkeletonOverlay/index.js.map +1 -0
  27. package/dist/components/styles.js +270 -0
  28. package/dist/components/styles.js.map +1 -0
  29. package/dist/components/utils/drawingUtils.js +45 -0
  30. package/dist/components/utils/drawingUtils.js.map +1 -0
  31. package/dist/engine/angleCalculator.js +196 -0
  32. package/dist/engine/angleCalculator.js.map +1 -0
  33. package/dist/engine/directionAnalyzer.js +97 -0
  34. package/dist/engine/directionAnalyzer.js.map +1 -0
  35. package/dist/engine/eventBus.js +45 -0
  36. package/dist/engine/eventBus.js.map +1 -0
  37. package/dist/engine/guidedAssessmentManager.js +252 -0
  38. package/dist/engine/guidedAssessmentManager.js.map +1 -0
  39. package/dist/engine/motionDetector.js +127 -0
  40. package/dist/engine/motionDetector.js.map +1 -0
  41. package/dist/engine/poseEngine.js +131 -0
  42. package/dist/engine/poseEngine.js.map +1 -0
  43. package/dist/engine.d.ts +153 -0
  44. package/dist/engine.js +10 -0
  45. package/dist/engine.js.map +1 -0
  46. package/dist/hooks/usePoseDetection.js +278 -0
  47. package/dist/hooks/usePoseDetection.js.map +1 -0
  48. package/dist/hooks/usePoseEngine.js +83 -0
  49. package/dist/hooks/usePoseEngine.js.map +1 -0
  50. package/dist/index.d.ts +596 -0
  51. package/dist/index.js +29 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/node_modules/.pnpm/@mediapipe_camera_utils@0.3.1675466862/node_modules/@mediapipe/camera_utils/camera_utils.js +377 -0
  54. package/dist/node_modules/.pnpm/@mediapipe_camera_utils@0.3.1675466862/node_modules/@mediapipe/camera_utils/camera_utils.js.map +1 -0
  55. package/dist/node_modules/.pnpm/@mediapipe_drawing_utils@0.3.1675466124/node_modules/@mediapipe/drawing_utils/drawing_utils.js +112 -0
  56. package/dist/node_modules/.pnpm/@mediapipe_drawing_utils@0.3.1675466124/node_modules/@mediapipe/drawing_utils/drawing_utils.js.map +1 -0
  57. package/dist/node_modules/.pnpm/@mediapipe_pose@0.5.1675469404/node_modules/@mediapipe/pose/pose.js +1796 -0
  58. package/dist/node_modules/.pnpm/@mediapipe_pose@0.5.1675469404/node_modules/@mediapipe/pose/pose.js.map +1 -0
  59. package/dist/types/index.js +5 -0
  60. package/dist/types/index.js.map +1 -0
  61. package/package.json +47 -0
@@ -0,0 +1,208 @@
1
+ import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
+ import { useRef as y, useState as m } from "react";
3
+ import { usePoseEngine as w } from "../../hooks/usePoseEngine.js";
4
+ import { SkeletonOverlay as C } from "../SkeletonOverlay/index.js";
5
+ import { AnglePanel as R } from "../AnglePanel/index.js";
6
+ /* empty css */
7
+ const V = ({
8
+ engineOptions: i = {},
9
+ width: o = 640,
10
+ height: s = 480,
11
+ showAnglePanel: v = !0,
12
+ showROM: p = !0,
13
+ drawingOptions: k = {},
14
+ className: N = "",
15
+ onStart: f,
16
+ onStop: L
17
+ }) => {
18
+ const n = y(null), [g, l] = m(null), [c, d] = m(!1), { start: z, stop: b, isRunning: a, angles: u, romData: M, error: h } = w({
19
+ ...i,
20
+ onResults: (r) => {
21
+ l(r), i.onResults?.(r);
22
+ }
23
+ }), j = async () => {
24
+ if (n.current)
25
+ try {
26
+ await z(n.current), d(!0), f?.();
27
+ } catch (r) {
28
+ console.error("Failed to start pose detection:", r);
29
+ }
30
+ }, x = () => {
31
+ b(), d(!1), l(null), L?.();
32
+ };
33
+ return /* @__PURE__ */ t("div", { className: `rom-visualizer ${N}`, children: [
34
+ /* @__PURE__ */ t("div", { className: "visualizer-container", children: [
35
+ /* @__PURE__ */ t("div", { className: "video-container", style: { width: o, height: s }, children: [
36
+ /* @__PURE__ */ e(
37
+ "video",
38
+ {
39
+ ref: n,
40
+ className: "video-element",
41
+ width: o,
42
+ height: s,
43
+ autoPlay: !0,
44
+ playsInline: !0,
45
+ muted: !0
46
+ }
47
+ ),
48
+ a && /* @__PURE__ */ e(
49
+ C,
50
+ {
51
+ results: g,
52
+ angles: u,
53
+ width: o,
54
+ height: s,
55
+ drawingOptions: k
56
+ }
57
+ ),
58
+ !c && /* @__PURE__ */ e("div", { className: "video-overlay", children: /* @__PURE__ */ t("div", { className: "overlay-content", children: [
59
+ /* @__PURE__ */ e(
60
+ "svg",
61
+ {
62
+ className: "camera-icon",
63
+ fill: "none",
64
+ stroke: "currentColor",
65
+ viewBox: "0 0 24 24",
66
+ width: "64",
67
+ height: "64",
68
+ children: /* @__PURE__ */ e(
69
+ "path",
70
+ {
71
+ strokeLinecap: "round",
72
+ strokeLinejoin: "round",
73
+ strokeWidth: 2,
74
+ d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
75
+ }
76
+ )
77
+ }
78
+ ),
79
+ /* @__PURE__ */ e("p", { children: "点击开始按钮启动摄像头" })
80
+ ] }) })
81
+ ] }),
82
+ v && /* @__PURE__ */ e(
83
+ R,
84
+ {
85
+ angles: u,
86
+ romData: M,
87
+ showROM: p,
88
+ className: "visualizer-angle-panel"
89
+ }
90
+ )
91
+ ] }),
92
+ /* @__PURE__ */ t("div", { className: "controls", children: [
93
+ c ? /* @__PURE__ */ t(
94
+ "button",
95
+ {
96
+ className: "control-button stop-button",
97
+ onClick: x,
98
+ children: [
99
+ /* @__PURE__ */ t(
100
+ "svg",
101
+ {
102
+ className: "button-icon",
103
+ fill: "none",
104
+ stroke: "currentColor",
105
+ viewBox: "0 0 24 24",
106
+ width: "20",
107
+ height: "20",
108
+ children: [
109
+ /* @__PURE__ */ e(
110
+ "path",
111
+ {
112
+ strokeLinecap: "round",
113
+ strokeLinejoin: "round",
114
+ strokeWidth: 2,
115
+ d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
116
+ }
117
+ ),
118
+ /* @__PURE__ */ e(
119
+ "path",
120
+ {
121
+ strokeLinecap: "round",
122
+ strokeLinejoin: "round",
123
+ strokeWidth: 2,
124
+ d: "M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"
125
+ }
126
+ )
127
+ ]
128
+ }
129
+ ),
130
+ "停止检测"
131
+ ]
132
+ }
133
+ ) : /* @__PURE__ */ t(
134
+ "button",
135
+ {
136
+ className: "control-button start-button",
137
+ onClick: j,
138
+ disabled: a,
139
+ children: [
140
+ /* @__PURE__ */ t(
141
+ "svg",
142
+ {
143
+ className: "button-icon",
144
+ fill: "none",
145
+ stroke: "currentColor",
146
+ viewBox: "0 0 24 24",
147
+ width: "20",
148
+ height: "20",
149
+ children: [
150
+ /* @__PURE__ */ e(
151
+ "path",
152
+ {
153
+ strokeLinecap: "round",
154
+ strokeLinejoin: "round",
155
+ strokeWidth: 2,
156
+ d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
157
+ }
158
+ ),
159
+ /* @__PURE__ */ e(
160
+ "path",
161
+ {
162
+ strokeLinecap: "round",
163
+ strokeLinejoin: "round",
164
+ strokeWidth: 2,
165
+ d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
166
+ }
167
+ )
168
+ ]
169
+ }
170
+ ),
171
+ "开始检测"
172
+ ]
173
+ }
174
+ ),
175
+ a && /* @__PURE__ */ t("div", { className: "status-indicator", children: [
176
+ /* @__PURE__ */ e("span", { className: "status-dot" }),
177
+ /* @__PURE__ */ e("span", { className: "status-text", children: "检测中" })
178
+ ] })
179
+ ] }),
180
+ h && /* @__PURE__ */ t("div", { className: "error-message", children: [
181
+ /* @__PURE__ */ e(
182
+ "svg",
183
+ {
184
+ className: "error-icon",
185
+ fill: "none",
186
+ stroke: "currentColor",
187
+ viewBox: "0 0 24 24",
188
+ width: "20",
189
+ height: "20",
190
+ children: /* @__PURE__ */ e(
191
+ "path",
192
+ {
193
+ strokeLinecap: "round",
194
+ strokeLinejoin: "round",
195
+ strokeWidth: 2,
196
+ d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
197
+ }
198
+ )
199
+ }
200
+ ),
201
+ /* @__PURE__ */ e("span", { children: h.message })
202
+ ] })
203
+ ] });
204
+ };
205
+ export {
206
+ V as ROMVisualizer
207
+ };
208
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/ROMVisualizer/index.tsx"],"sourcesContent":["import React, { useRef, useState } from 'react'\nimport type { Results } from '@mediapipe/pose'\nimport { usePoseEngine, UsePoseEngineOptions } from '../../hooks/usePoseEngine'\nimport { SkeletonOverlay } from '../SkeletonOverlay'\nimport { AnglePanel } from '../AnglePanel'\nimport type { DrawingOptions } from '../utils/drawingUtils'\nimport './index.css'\n\nexport interface ROMVisualizerProps {\n engineOptions?: UsePoseEngineOptions\n width?: number\n height?: number\n showAnglePanel?: boolean\n showROM?: boolean\n drawingOptions?: DrawingOptions\n className?: string\n onStart?: () => void\n onStop?: () => void\n}\n\nexport const ROMVisualizer: React.FC<ROMVisualizerProps> = ({\n engineOptions = {},\n width = 640,\n height = 480,\n showAnglePanel = true,\n showROM = true,\n drawingOptions = {},\n className = '',\n onStart,\n onStop,\n}) => {\n const videoRef = useRef<HTMLVideoElement>(null)\n const [results, setResults] = useState<Results | null>(null)\n const [isStarted, setIsStarted] = useState(false)\n\n const { start, stop, isRunning, angles, romData, error } = usePoseEngine({\n ...engineOptions,\n onResults: (newResults) => {\n setResults(newResults)\n engineOptions.onResults?.(newResults)\n },\n })\n\n const handleStart = async () => {\n if (!videoRef.current) return\n\n try {\n await start(videoRef.current)\n setIsStarted(true)\n onStart?.()\n } catch (err) {\n console.error('Failed to start pose detection:', err)\n }\n }\n\n const handleStop = () => {\n stop()\n setIsStarted(false)\n setResults(null)\n onStop?.()\n }\n\n return (\n <div className={`rom-visualizer ${className}`}>\n <div className=\"visualizer-container\">\n <div className=\"video-container\" style={{ width, height }}>\n <video\n ref={videoRef}\n className=\"video-element\"\n width={width}\n height={height}\n autoPlay\n playsInline\n muted\n />\n {isRunning && (\n <SkeletonOverlay\n results={results}\n angles={angles}\n width={width}\n height={height}\n drawingOptions={drawingOptions}\n />\n )}\n {!isStarted && (\n <div className=\"video-overlay\">\n <div className=\"overlay-content\">\n <svg\n className=\"camera-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n width=\"64\"\n height=\"64\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n <p>点击开始按钮启动摄像头</p>\n </div>\n </div>\n )}\n </div>\n\n {showAnglePanel && (\n <AnglePanel\n angles={angles}\n romData={romData}\n showROM={showROM}\n className=\"visualizer-angle-panel\"\n />\n )}\n </div>\n\n <div className=\"controls\">\n {!isStarted ? (\n <button\n className=\"control-button start-button\"\n onClick={handleStart}\n disabled={isRunning}\n >\n <svg\n className=\"button-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n width=\"20\"\n height=\"20\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n 开始检测\n </button>\n ) : (\n <button\n className=\"control-button stop-button\"\n onClick={handleStop}\n >\n <svg\n className=\"button-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n width=\"20\"\n height=\"20\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z\"\n />\n </svg>\n 停止检测\n </button>\n )}\n\n {isRunning && (\n <div className=\"status-indicator\">\n <span className=\"status-dot\"></span>\n <span className=\"status-text\">检测中</span>\n </div>\n )}\n </div>\n\n {error && (\n <div className=\"error-message\">\n <svg\n className=\"error-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n width=\"20\"\n height=\"20\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span>{error.message}</span>\n </div>\n )}\n </div>\n )\n}\n"],"names":["ROMVisualizer","engineOptions","width","height","showAnglePanel","showROM","drawingOptions","className","onStart","onStop","videoRef","useRef","results","setResults","useState","isStarted","setIsStarted","start","stop","isRunning","angles","romData","error","usePoseEngine","newResults","handleStart","err","handleStop","jsxs","jsx","SkeletonOverlay","AnglePanel"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ .skeleton-overlay{position:absolute;top:0;left:0;pointer-events:none;z-index:10;transform:scaleX(-1)}
@@ -0,0 +1,45 @@
1
+ import { jsx as d } from "react/jsx-runtime";
2
+ import { useRef as z, useEffect as x } from "react";
3
+ import { resizeCanvas as C, clearCanvas as L, drawPoseLandmarks as b, drawAngle as A } from "../utils/drawingUtils.js";
4
+ /* empty css */
5
+ const P = ({
6
+ results: e,
7
+ angles: f = [],
8
+ width: n = 640,
9
+ height: o = 480,
10
+ drawingOptions: a = {},
11
+ className: u = ""
12
+ }) => {
13
+ const m = z(null);
14
+ return x(() => {
15
+ const s = m.current;
16
+ s && C(s, n, o);
17
+ }, [n, o]), x(() => {
18
+ const s = m.current;
19
+ if (!s) return;
20
+ const c = s.getContext("2d");
21
+ if (!c || (L(c), !e || !e.poseLandmarks)) return;
22
+ const t = e.poseLandmarks.map((r) => ({
23
+ x: r.x,
24
+ y: r.y,
25
+ z: r.z,
26
+ visibility: r.visibility
27
+ }));
28
+ b(c, t, a), a.showAngles && f.length > 0 && f.forEach((r) => {
29
+ const [k, l, y] = r.points, v = t[k], i = t[l], p = t[y];
30
+ v && i && p && A(c, v, i, p, r.angle, n, o, a);
31
+ });
32
+ }, [e, f, n, o, a]), /* @__PURE__ */ d(
33
+ "canvas",
34
+ {
35
+ ref: m,
36
+ className: `skeleton-overlay ${u}`,
37
+ width: n,
38
+ height: o
39
+ }
40
+ );
41
+ };
42
+ export {
43
+ P as SkeletonOverlay
44
+ };
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/SkeletonOverlay/index.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react'\nimport type { Results } from '@mediapipe/pose'\nimport type { JointAngle, PosePoint } from '../../types'\nimport { drawPoseLandmarks, drawAngle, clearCanvas, resizeCanvas, DrawingOptions } from '../utils/drawingUtils'\nimport './index.css'\n\nexport interface SkeletonOverlayProps {\n results: Results | null\n angles?: JointAngle[]\n width?: number\n height?: number\n drawingOptions?: DrawingOptions\n className?: string\n}\n\nexport const SkeletonOverlay: React.FC<SkeletonOverlayProps> = ({\n results,\n angles = [],\n width = 640,\n height = 480,\n drawingOptions = {},\n className = '',\n}) => {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n resizeCanvas(canvas, width, height)\n }, [width, height])\n\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n\n clearCanvas(ctx)\n\n if (!results || !results.poseLandmarks) return\n\n const landmarks: PosePoint[] = results.poseLandmarks.map((lm) => ({\n x: lm.x,\n y: lm.y,\n z: lm.z,\n visibility: lm.visibility,\n }))\n\n drawPoseLandmarks(ctx, landmarks, drawingOptions)\n\n if (drawingOptions.showAngles && angles.length > 0) {\n angles.forEach((angleData) => {\n const [idx1, idx2, idx3] = angleData.points\n const p1 = landmarks[idx1]\n const p2 = landmarks[idx2]\n const p3 = landmarks[idx3]\n\n if (p1 && p2 && p3) {\n drawAngle(ctx, p1, p2, p3, angleData.angle, width, height, drawingOptions)\n }\n })\n }\n }, [results, angles, width, height, drawingOptions])\n\n return (\n <canvas\n ref={canvasRef}\n className={`skeleton-overlay ${className}`}\n width={width}\n height={height}\n />\n )\n}\n"],"names":["SkeletonOverlay","results","angles","width","height","drawingOptions","className","canvasRef","useRef","useEffect","canvas","resizeCanvas","ctx","clearCanvas","landmarks","lm","drawPoseLandmarks","angleData","idx1","idx2","idx3","p1","p2","p3","drawAngle","jsx"],"mappings":";;;;AAeO,MAAMA,IAAkD,CAAC;AAAA,EAC9D,SAAAC;AAAA,EACA,QAAAC,IAAS,CAAA;AAAA,EACT,OAAAC,IAAQ;AAAA,EACR,QAAAC,IAAS;AAAA,EACT,gBAAAC,IAAiB,CAAA;AAAA,EACjB,WAAAC,IAAY;AACd,MAAM;AACJ,QAAMC,IAAYC,EAA0B,IAAI;AAEhD,SAAAC,EAAU,MAAM;AACd,UAAMC,IAASH,EAAU;AACzB,IAAKG,KAELC,EAAaD,GAAQP,GAAOC,CAAM;AAAA,EACpC,GAAG,CAACD,GAAOC,CAAM,CAAC,GAElBK,EAAU,MAAM;AACd,UAAMC,IAASH,EAAU;AACzB,QAAI,CAACG,EAAQ;AAEb,UAAME,IAAMF,EAAO,WAAW,IAAI;AAKlC,QAJI,CAACE,MAELC,EAAYD,CAAG,GAEX,CAACX,KAAW,CAACA,EAAQ,eAAe;AAExC,UAAMa,IAAyBb,EAAQ,cAAc,IAAI,CAACc,OAAQ;AAAA,MAChE,GAAGA,EAAG;AAAA,MACN,GAAGA,EAAG;AAAA,MACN,GAAGA,EAAG;AAAA,MACN,YAAYA,EAAG;AAAA,IAAA,EACf;AAEF,IAAAC,EAAkBJ,GAAKE,GAAWT,CAAc,GAE5CA,EAAe,cAAcH,EAAO,SAAS,KAC/CA,EAAO,QAAQ,CAACe,MAAc;AAC5B,YAAM,CAACC,GAAMC,GAAMC,CAAI,IAAIH,EAAU,QAC/BI,IAAKP,EAAUI,CAAI,GACnBI,IAAKR,EAAUK,CAAI,GACnBI,IAAKT,EAAUM,CAAI;AAEzB,MAAIC,KAAMC,KAAMC,KACdC,EAAUZ,GAAKS,GAAIC,GAAIC,GAAIN,EAAU,OAAOd,GAAOC,GAAQC,CAAc;AAAA,IAE7E,CAAC;AAAA,EAEL,GAAG,CAACJ,GAASC,GAAQC,GAAOC,GAAQC,CAAc,CAAC,GAGjD,gBAAAoB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAKlB;AAAA,MACL,WAAW,oBAAoBD,CAAS;AAAA,MACxC,OAAAH;AAAA,MACA,QAAAC;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -0,0 +1,270 @@
1
+ function t() {
2
+ if (typeof document > "u") return;
3
+ const r = "pose-ai-core-styles";
4
+ if (document.getElementById(r)) return;
5
+ const e = document.createElement("style");
6
+ e.id = r, e.textContent = `
7
+ /* SkeletonOverlay */
8
+ .skeleton-overlay {
9
+ position: absolute;
10
+ top: 0;
11
+ left: 0;
12
+ pointer-events: none;
13
+ z-index: 10;
14
+ transform: scaleX(-1);
15
+ }
16
+
17
+ /* ROMVisualizer */
18
+ .rom-visualizer {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 1.5rem;
22
+ width: 100%;
23
+ max-width: 1200px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ .visualizer-container {
28
+ display: flex;
29
+ gap: 1.5rem;
30
+ flex-wrap: wrap;
31
+ }
32
+
33
+ .video-container {
34
+ position: relative;
35
+ background: #000;
36
+ border-radius: 12px;
37
+ overflow: hidden;
38
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
39
+ }
40
+
41
+ .video-element {
42
+ display: block;
43
+ width: 100%;
44
+ height: 100%;
45
+ transform: scaleX(-1);
46
+ }
47
+
48
+ .video-overlay {
49
+ position: absolute;
50
+ top: 0;
51
+ left: 0;
52
+ right: 0;
53
+ bottom: 0;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ background: rgba(0, 0, 0, 0.7);
58
+ z-index: 5;
59
+ }
60
+
61
+ .overlay-content {
62
+ text-align: center;
63
+ color: white;
64
+ }
65
+
66
+ .camera-icon {
67
+ margin: 0 auto 1rem;
68
+ opacity: 0.8;
69
+ }
70
+
71
+ .controls {
72
+ display: flex;
73
+ gap: 1rem;
74
+ justify-content: center;
75
+ flex-wrap: wrap;
76
+ }
77
+
78
+ .control-button {
79
+ padding: 0.75rem 2rem;
80
+ font-size: 1rem;
81
+ font-weight: 600;
82
+ border: none;
83
+ border-radius: 8px;
84
+ cursor: pointer;
85
+ transition: all 0.2s;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 0.5rem;
89
+ }
90
+
91
+ .control-button:hover {
92
+ transform: translateY(-2px);
93
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
94
+ }
95
+
96
+ .control-button:active {
97
+ transform: translateY(0);
98
+ }
99
+
100
+ .start-button {
101
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
102
+ color: white;
103
+ }
104
+
105
+ .stop-button {
106
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
107
+ color: white;
108
+ }
109
+
110
+ .status-indicator {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: 0.5rem;
114
+ padding: 0.5rem 1rem;
115
+ background: rgba(255, 255, 255, 0.1);
116
+ border-radius: 20px;
117
+ color: white;
118
+ font-size: 0.9rem;
119
+ }
120
+
121
+ .status-dot {
122
+ width: 8px;
123
+ height: 8px;
124
+ border-radius: 50%;
125
+ background: #4ade80;
126
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
127
+ }
128
+
129
+ @keyframes pulse {
130
+ 0%, 100% {
131
+ opacity: 1;
132
+ }
133
+ 50% {
134
+ opacity: 0.5;
135
+ }
136
+ }
137
+
138
+ .error-message {
139
+ padding: 1rem;
140
+ background: #fee;
141
+ border: 1px solid #fcc;
142
+ border-radius: 8px;
143
+ color: #c00;
144
+ margin-top: 1rem;
145
+ }
146
+
147
+ /* AnglePanel */
148
+ .angle-panel {
149
+ flex: 1;
150
+ min-width: 300px;
151
+ background: white;
152
+ border-radius: 12px;
153
+ padding: 1.5rem;
154
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
155
+ max-height: 600px;
156
+ overflow-y: auto;
157
+ }
158
+
159
+ .angle-panel h3 {
160
+ margin: 0 0 1rem 0;
161
+ color: #333;
162
+ font-size: 1.2rem;
163
+ font-weight: 600;
164
+ }
165
+
166
+ .angle-list {
167
+ display: flex;
168
+ flex-direction: column;
169
+ gap: 0.75rem;
170
+ }
171
+
172
+ .angle-item {
173
+ padding: 1rem;
174
+ background: #f8f9fa;
175
+ border-radius: 8px;
176
+ transition: all 0.2s;
177
+ }
178
+
179
+ .angle-item:hover {
180
+ background: #e9ecef;
181
+ transform: translateX(4px);
182
+ }
183
+
184
+ .angle-header {
185
+ display: flex;
186
+ justify-content: space-between;
187
+ align-items: center;
188
+ margin-bottom: 0.5rem;
189
+ }
190
+
191
+ .angle-name {
192
+ font-weight: 600;
193
+ color: #495057;
194
+ text-transform: capitalize;
195
+ }
196
+
197
+ .angle-value {
198
+ font-size: 1.25rem;
199
+ font-weight: 700;
200
+ color: #667eea;
201
+ }
202
+
203
+ .rom-info {
204
+ display: flex;
205
+ flex-direction: column;
206
+ gap: 0.25rem;
207
+ font-size: 0.85rem;
208
+ color: #6c757d;
209
+ margin-top: 0.5rem;
210
+ }
211
+
212
+ .rom-range {
213
+ display: flex;
214
+ justify-content: space-between;
215
+ }
216
+
217
+ .rom-progress {
218
+ width: 100%;
219
+ height: 6px;
220
+ background: #e9ecef;
221
+ border-radius: 3px;
222
+ overflow: hidden;
223
+ margin-top: 0.5rem;
224
+ }
225
+
226
+ .rom-progress-bar {
227
+ height: 100%;
228
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
229
+ transition: width 0.3s ease;
230
+ }
231
+
232
+ .angle-panel::-webkit-scrollbar {
233
+ width: 8px;
234
+ }
235
+
236
+ .angle-panel::-webkit-scrollbar-track {
237
+ background: #f1f1f1;
238
+ border-radius: 4px;
239
+ }
240
+
241
+ .angle-panel::-webkit-scrollbar-thumb {
242
+ background: #667eea;
243
+ border-radius: 4px;
244
+ }
245
+
246
+ .angle-panel::-webkit-scrollbar-thumb:hover {
247
+ background: #5568d3;
248
+ }
249
+
250
+ @media (max-width: 768px) {
251
+ .rom-visualizer {
252
+ gap: 1rem;
253
+ }
254
+
255
+ .visualizer-container {
256
+ flex-direction: column;
257
+ }
258
+
259
+ .angle-panel {
260
+ min-width: auto;
261
+ max-height: 400px;
262
+ }
263
+ }
264
+ `, document.head.appendChild(e);
265
+ }
266
+ typeof window < "u" && t();
267
+ export {
268
+ t as injectStyles
269
+ };
270
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sources":["../../src/components/styles.ts"],"sourcesContent":["// 将所有组件样式集中管理,确保样式被正确加载\n\nexport function injectStyles() {\n if (typeof document === 'undefined') return\n \n const styleId = 'pose-ai-core-styles'\n if (document.getElementById(styleId)) return\n\n const style = document.createElement('style')\n style.id = styleId\n style.textContent = `\n/* SkeletonOverlay */\n.skeleton-overlay {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n z-index: 10;\n transform: scaleX(-1);\n}\n\n/* ROMVisualizer */\n.rom-visualizer {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n width: 100%;\n max-width: 1200px;\n margin: 0 auto;\n}\n\n.visualizer-container {\n display: flex;\n gap: 1.5rem;\n flex-wrap: wrap;\n}\n\n.video-container {\n position: relative;\n background: #000;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n}\n\n.video-element {\n display: block;\n width: 100%;\n height: 100%;\n transform: scaleX(-1);\n}\n\n.video-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.7);\n z-index: 5;\n}\n\n.overlay-content {\n text-align: center;\n color: white;\n}\n\n.camera-icon {\n margin: 0 auto 1rem;\n opacity: 0.8;\n}\n\n.controls {\n display: flex;\n gap: 1rem;\n justify-content: center;\n flex-wrap: wrap;\n}\n\n.control-button {\n padding: 0.75rem 2rem;\n font-size: 1rem;\n font-weight: 600;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.control-button:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.control-button:active {\n transform: translateY(0);\n}\n\n.start-button {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n}\n\n.stop-button {\n background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);\n color: white;\n}\n\n.status-indicator {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 20px;\n color: white;\n font-size: 0.9rem;\n}\n\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #4ade80;\n animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.error-message {\n padding: 1rem;\n background: #fee;\n border: 1px solid #fcc;\n border-radius: 8px;\n color: #c00;\n margin-top: 1rem;\n}\n\n/* AnglePanel */\n.angle-panel {\n flex: 1;\n min-width: 300px;\n background: white;\n border-radius: 12px;\n padding: 1.5rem;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-height: 600px;\n overflow-y: auto;\n}\n\n.angle-panel h3 {\n margin: 0 0 1rem 0;\n color: #333;\n font-size: 1.2rem;\n font-weight: 600;\n}\n\n.angle-list {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.angle-item {\n padding: 1rem;\n background: #f8f9fa;\n border-radius: 8px;\n transition: all 0.2s;\n}\n\n.angle-item:hover {\n background: #e9ecef;\n transform: translateX(4px);\n}\n\n.angle-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.5rem;\n}\n\n.angle-name {\n font-weight: 600;\n color: #495057;\n text-transform: capitalize;\n}\n\n.angle-value {\n font-size: 1.25rem;\n font-weight: 700;\n color: #667eea;\n}\n\n.rom-info {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n font-size: 0.85rem;\n color: #6c757d;\n margin-top: 0.5rem;\n}\n\n.rom-range {\n display: flex;\n justify-content: space-between;\n}\n\n.rom-progress {\n width: 100%;\n height: 6px;\n background: #e9ecef;\n border-radius: 3px;\n overflow: hidden;\n margin-top: 0.5rem;\n}\n\n.rom-progress-bar {\n height: 100%;\n background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);\n transition: width 0.3s ease;\n}\n\n.angle-panel::-webkit-scrollbar {\n width: 8px;\n}\n\n.angle-panel::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 4px;\n}\n\n.angle-panel::-webkit-scrollbar-thumb {\n background: #667eea;\n border-radius: 4px;\n}\n\n.angle-panel::-webkit-scrollbar-thumb:hover {\n background: #5568d3;\n}\n\n@media (max-width: 768px) {\n .rom-visualizer {\n gap: 1rem;\n }\n\n .visualizer-container {\n flex-direction: column;\n }\n\n .angle-panel {\n min-width: auto;\n max-height: 400px;\n }\n}\n`\n\n document.head.appendChild(style)\n}\n\n// 自动注入样式\nif (typeof window !== 'undefined') {\n injectStyles()\n}\n"],"names":["injectStyles","styleId","style"],"mappings":"AAEO,SAASA,IAAe;AAC7B,MAAI,OAAO,WAAa,IAAa;AAErC,QAAMC,IAAU;AAChB,MAAI,SAAS,eAAeA,CAAO,EAAG;AAEtC,QAAMC,IAAQ,SAAS,cAAc,OAAO;AAC5C,EAAAA,EAAM,KAAKD,GACXC,EAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAoQpB,SAAS,KAAK,YAAYA,CAAK;AACjC;AAGI,OAAO,SAAW,OACpBF,EAAA;"}
@@ -0,0 +1,45 @@
1
+ import { d as m } from "../../_virtual/drawing_utils.js";
2
+ import { p as T } from "../../_virtual/pose.js";
3
+ const g = {
4
+ showLandmarks: !0,
5
+ showConnections: !0,
6
+ showAngles: !0,
7
+ landmarkColor: "#00FF00",
8
+ connectionColor: "#00FF00",
9
+ angleColor: "#FF0000",
10
+ landmarkRadius: 4,
11
+ lineWidth: 2
12
+ };
13
+ function L(o, e, s = {}) {
14
+ const n = { ...g, ...s };
15
+ n.showConnections && m.drawConnectors(o, e, T.POSE_CONNECTIONS, {
16
+ color: n.connectionColor,
17
+ lineWidth: n.lineWidth
18
+ }), n.showLandmarks && m.drawLandmarks(o, e, {
19
+ color: n.landmarkColor,
20
+ radius: n.landmarkRadius,
21
+ lineWidth: n.lineWidth
22
+ });
23
+ }
24
+ function M(o, e, s, n, k, r, i, p = {}) {
25
+ const l = { ...g, ...p }, c = e.x * r, f = e.y * i, t = s.x * r, a = s.y * i, u = n.x * r, w = n.y * i;
26
+ o.save(), o.strokeStyle = l.angleColor, o.lineWidth = l.lineWidth, o.fillStyle = l.angleColor, o.font = "16px Arial", o.beginPath(), o.moveTo(c, f), o.lineTo(t, a), o.lineTo(u, w), o.stroke();
27
+ const d = 30, h = Math.atan2(f - a, c - t), C = Math.atan2(w - a, u - t);
28
+ o.beginPath(), o.arc(t, a, d, h, C, !1), o.stroke();
29
+ const y = t + Math.cos((h + C) / 2) * (d + 20), F = a + Math.sin((h + C) / 2) * (d + 20);
30
+ o.fillText(`${k.toFixed(1)}°`, y, F), o.restore();
31
+ }
32
+ function O(o) {
33
+ o.clearRect(0, 0, o.canvas.width, o.canvas.height);
34
+ }
35
+ function P(o, e, s) {
36
+ o.width = e, o.height = s;
37
+ }
38
+ export {
39
+ O as clearCanvas,
40
+ g as defaultDrawingOptions,
41
+ M as drawAngle,
42
+ L as drawPoseLandmarks,
43
+ P as resizeCanvas
44
+ };
45
+ //# sourceMappingURL=drawingUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawingUtils.js","sources":["../../../src/components/utils/drawingUtils.ts"],"sourcesContent":["import { drawConnectors, drawLandmarks } from '@mediapipe/drawing_utils'\nimport { POSE_CONNECTIONS } from '@mediapipe/pose'\nimport type { PosePoint } from '../../types'\n\nexport interface DrawingOptions {\n showLandmarks?: boolean\n showConnections?: boolean\n showAngles?: boolean\n landmarkColor?: string\n connectionColor?: string\n angleColor?: string\n landmarkRadius?: number\n lineWidth?: number\n}\n\nexport const defaultDrawingOptions: Required<DrawingOptions> = {\n showLandmarks: true,\n showConnections: true,\n showAngles: true,\n landmarkColor: '#00FF00',\n connectionColor: '#00FF00',\n angleColor: '#FF0000',\n landmarkRadius: 4,\n lineWidth: 2,\n}\n\nexport function drawPoseLandmarks(\n ctx: CanvasRenderingContext2D,\n landmarks: PosePoint[],\n options: DrawingOptions = {}\n): void {\n const opts = { ...defaultDrawingOptions, ...options }\n\n if (opts.showConnections) {\n drawConnectors(ctx, landmarks, POSE_CONNECTIONS, {\n color: opts.connectionColor,\n lineWidth: opts.lineWidth,\n })\n }\n\n if (opts.showLandmarks) {\n drawLandmarks(ctx, landmarks, {\n color: opts.landmarkColor,\n radius: opts.landmarkRadius,\n lineWidth: opts.lineWidth,\n })\n }\n}\n\nexport function drawAngle(\n ctx: CanvasRenderingContext2D,\n p1: PosePoint,\n p2: PosePoint,\n p3: PosePoint,\n angle: number,\n canvasWidth: number,\n canvasHeight: number,\n options: DrawingOptions = {}\n): void {\n const opts = { ...defaultDrawingOptions, ...options }\n\n const x1 = p1.x * canvasWidth\n const y1 = p1.y * canvasHeight\n const x2 = p2.x * canvasWidth\n const y2 = p2.y * canvasHeight\n const x3 = p3.x * canvasWidth\n const y3 = p3.y * canvasHeight\n\n ctx.save()\n ctx.strokeStyle = opts.angleColor\n ctx.lineWidth = opts.lineWidth\n ctx.fillStyle = opts.angleColor\n ctx.font = '16px Arial'\n\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.lineTo(x3, y3)\n ctx.stroke()\n\n const arcRadius = 30\n const angle1 = Math.atan2(y1 - y2, x1 - x2)\n const angle2 = Math.atan2(y3 - y2, x3 - x2)\n\n ctx.beginPath()\n ctx.arc(x2, y2, arcRadius, angle1, angle2, false)\n ctx.stroke()\n\n const textX = x2 + Math.cos((angle1 + angle2) / 2) * (arcRadius + 20)\n const textY = y2 + Math.sin((angle1 + angle2) / 2) * (arcRadius + 20)\n ctx.fillText(`${angle.toFixed(1)}°`, textX, textY)\n\n ctx.restore()\n}\n\nexport function clearCanvas(ctx: CanvasRenderingContext2D): void {\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)\n}\n\nexport function resizeCanvas(\n canvas: HTMLCanvasElement,\n width: number,\n height: number\n): void {\n canvas.width = width\n canvas.height = height\n}\n"],"names":["defaultDrawingOptions","drawPoseLandmarks","ctx","landmarks","options","opts","drawConnectors","POSE_CONNECTIONS","drawLandmarks","drawAngle","p1","p2","p3","angle","canvasWidth","canvasHeight","x1","y1","x2","y2","x3","y3","arcRadius","angle1","angle2","textX","textY","clearCanvas","resizeCanvas","canvas","width","height"],"mappings":";;AAeO,MAAMA,IAAkD;AAAA,EAC7D,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,WAAW;AACb;AAEO,SAASC,EACdC,GACAC,GACAC,IAA0B,CAAA,GACpB;AACN,QAAMC,IAAO,EAAE,GAAGL,GAAuB,GAAGI,EAAA;AAE5C,EAAIC,EAAK,mBACPC,iBAAeJ,GAAKC,GAAWI,oBAAkB;AAAA,IAC/C,OAAOF,EAAK;AAAA,IACZ,WAAWA,EAAK;AAAA,EAAA,CACjB,GAGCA,EAAK,iBACPG,EAAAA,cAAcN,GAAKC,GAAW;AAAA,IAC5B,OAAOE,EAAK;AAAA,IACZ,QAAQA,EAAK;AAAA,IACb,WAAWA,EAAK;AAAA,EAAA,CACjB;AAEL;AAEO,SAASI,EACdP,GACAQ,GACAC,GACAC,GACAC,GACAC,GACAC,GACAX,IAA0B,IACpB;AACN,QAAMC,IAAO,EAAE,GAAGL,GAAuB,GAAGI,EAAA,GAEtCY,IAAKN,EAAG,IAAII,GACZG,IAAKP,EAAG,IAAIK,GACZG,IAAKP,EAAG,IAAIG,GACZK,IAAKR,EAAG,IAAII,GACZK,IAAKR,EAAG,IAAIE,GACZO,IAAKT,EAAG,IAAIG;AAElB,EAAAb,EAAI,KAAA,GACJA,EAAI,cAAcG,EAAK,YACvBH,EAAI,YAAYG,EAAK,WACrBH,EAAI,YAAYG,EAAK,YACrBH,EAAI,OAAO,cAEXA,EAAI,UAAA,GACJA,EAAI,OAAOc,GAAIC,CAAE,GACjBf,EAAI,OAAOgB,GAAIC,CAAE,GACjBjB,EAAI,OAAOkB,GAAIC,CAAE,GACjBnB,EAAI,OAAA;AAEJ,QAAMoB,IAAY,IACZC,IAAS,KAAK,MAAMN,IAAKE,GAAIH,IAAKE,CAAE,GACpCM,IAAS,KAAK,MAAMH,IAAKF,GAAIC,IAAKF,CAAE;AAE1C,EAAAhB,EAAI,UAAA,GACJA,EAAI,IAAIgB,GAAIC,GAAIG,GAAWC,GAAQC,GAAQ,EAAK,GAChDtB,EAAI,OAAA;AAEJ,QAAMuB,IAAQP,IAAK,KAAK,KAAKK,IAASC,KAAU,CAAC,KAAKF,IAAY,KAC5DI,IAAQP,IAAK,KAAK,KAAKI,IAASC,KAAU,CAAC,KAAKF,IAAY;AAClE,EAAApB,EAAI,SAAS,GAAGW,EAAM,QAAQ,CAAC,CAAC,KAAKY,GAAOC,CAAK,GAEjDxB,EAAI,QAAA;AACN;AAEO,SAASyB,EAAYzB,GAAqC;AAC/D,EAAAA,EAAI,UAAU,GAAG,GAAGA,EAAI,OAAO,OAAOA,EAAI,OAAO,MAAM;AACzD;AAEO,SAAS0B,EACdC,GACAC,GACAC,GACM;AACN,EAAAF,EAAO,QAAQC,GACfD,EAAO,SAASE;AAClB;"}