@cut-workbench/workbench 0.1.0 → 0.2.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.
@@ -0,0 +1,208 @@
1
+ // \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ let react = require("react");
23
+ react = __toESM(react, 1);
24
+ let _cut_workbench_core = require("@cut-workbench/core");
25
+ let clsx = require("clsx");
26
+ let tailwind_merge = require("tailwind-merge");
27
+ let react_jsx_runtime = require("react/jsx-runtime");
28
+ let class_variance_authority = require("class-variance-authority");
29
+ let radix_ui = require("radix-ui");
30
+ // src/lib/cn.ts
31
+ /**
32
+ * 合并 className:tailwind-merge 负责消解冲突,clsx 负责条件拼接。
33
+ */
34
+ function cn(...inputs) {
35
+ return (0, tailwind_merge.twMerge)((0, clsx.clsx)(inputs));
36
+ }
37
+ // src/components/ui/badge.tsx
38
+ var badgeVariants = (0, class_variance_authority.cva)("inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", {
39
+ variants: { variant: {
40
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
41
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
42
+ destructive: "bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
43
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
44
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
45
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
46
+ } },
47
+ defaultVariants: { variant: "default" }
48
+ });
49
+ function Badge({ className, variant = "default", asChild = false, ...props }) {
50
+ const Comp = asChild ? radix_ui.Slot.Root : "span";
51
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Comp, {
52
+ "data-slot": "badge",
53
+ "data-variant": variant,
54
+ className: cn(badgeVariants({ variant }), className),
55
+ ...props
56
+ });
57
+ }
58
+ // src/components/ui/card.tsx
59
+ function Card({ className, ...props }) {
60
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
61
+ "data-slot": "card",
62
+ className: cn("flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm", className),
63
+ ...props
64
+ });
65
+ }
66
+ function CardHeader({ className, ...props }) {
67
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
68
+ "data-slot": "card-header",
69
+ className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className),
70
+ ...props
71
+ });
72
+ }
73
+ function CardTitle({ className, ...props }) {
74
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
75
+ "data-slot": "card-title",
76
+ className: cn("leading-none font-semibold", className),
77
+ ...props
78
+ });
79
+ }
80
+ function CardDescription({ className, ...props }) {
81
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
82
+ "data-slot": "card-description",
83
+ className: cn("text-sm text-muted-foreground", className),
84
+ ...props
85
+ });
86
+ }
87
+ function CardContent({ className, ...props }) {
88
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
89
+ "data-slot": "card-content",
90
+ className: cn("px-6", className),
91
+ ...props
92
+ });
93
+ }
94
+ // src/lib/format.ts
95
+ /** 字节数 → 人类可读 */
96
+ function formatBytes(bytes) {
97
+ if (bytes < 1024) return `${bytes} B`;
98
+ const units = [
99
+ "KB",
100
+ "MB",
101
+ "GB"
102
+ ];
103
+ let value = bytes / 1024;
104
+ let i = 0;
105
+ while (value >= 1024 && i < units.length - 1) {
106
+ value /= 1024;
107
+ i += 1;
108
+ }
109
+ return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[i]}`;
110
+ }
111
+ /** 像素尺寸 → `1920×1080` */
112
+ function formatResolution(width, height) {
113
+ if (!width || !height) return "—";
114
+ return `${width}×${height}`;
115
+ }
116
+ /** 秒数 → `12.3s` */
117
+ function formatElapsed(seconds) {
118
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
119
+ return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
120
+ }
121
+ /** 微秒 → 秒(保留一位小数,用于表单展示) */
122
+ function formatSeconds(us) {
123
+ return (0, _cut_workbench_core.usToSec)(us).toFixed(2);
124
+ }
125
+ /** 百分比 0~1 → `42%` */
126
+ function formatPercent(ratio) {
127
+ return `${Math.round(ratio * 100)}%`;
128
+ }
129
+ Object.defineProperty(exports, "Badge", {
130
+ enumerable: true,
131
+ get: function() {
132
+ return Badge;
133
+ }
134
+ });
135
+ Object.defineProperty(exports, "Card", {
136
+ enumerable: true,
137
+ get: function() {
138
+ return Card;
139
+ }
140
+ });
141
+ Object.defineProperty(exports, "CardContent", {
142
+ enumerable: true,
143
+ get: function() {
144
+ return CardContent;
145
+ }
146
+ });
147
+ Object.defineProperty(exports, "CardDescription", {
148
+ enumerable: true,
149
+ get: function() {
150
+ return CardDescription;
151
+ }
152
+ });
153
+ Object.defineProperty(exports, "CardHeader", {
154
+ enumerable: true,
155
+ get: function() {
156
+ return CardHeader;
157
+ }
158
+ });
159
+ Object.defineProperty(exports, "CardTitle", {
160
+ enumerable: true,
161
+ get: function() {
162
+ return CardTitle;
163
+ }
164
+ });
165
+ Object.defineProperty(exports, "__toESM", {
166
+ enumerable: true,
167
+ get: function() {
168
+ return __toESM;
169
+ }
170
+ });
171
+ Object.defineProperty(exports, "cn", {
172
+ enumerable: true,
173
+ get: function() {
174
+ return cn;
175
+ }
176
+ });
177
+ Object.defineProperty(exports, "formatBytes", {
178
+ enumerable: true,
179
+ get: function() {
180
+ return formatBytes;
181
+ }
182
+ });
183
+ Object.defineProperty(exports, "formatElapsed", {
184
+ enumerable: true,
185
+ get: function() {
186
+ return formatElapsed;
187
+ }
188
+ });
189
+ Object.defineProperty(exports, "formatPercent", {
190
+ enumerable: true,
191
+ get: function() {
192
+ return formatPercent;
193
+ }
194
+ });
195
+ Object.defineProperty(exports, "formatResolution", {
196
+ enumerable: true,
197
+ get: function() {
198
+ return formatResolution;
199
+ }
200
+ });
201
+ Object.defineProperty(exports, "formatSeconds", {
202
+ enumerable: true,
203
+ get: function() {
204
+ return formatSeconds;
205
+ }
206
+ });
207
+
208
+ //# sourceMappingURL=format--DhqHMgX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format--DhqHMgX.cjs","names":["twMerge","cva","Slot","usToSec"],"sources":["../src/lib/cn.ts","../src/components/ui/badge.tsx","../src/components/ui/card.tsx","../src/lib/format.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/**\n * 合并 className:tailwind-merge 负责消解冲突,clsx 负责条件拼接。\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Slot } from \"radix-ui\"\n\nconst badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n secondary:\n \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n outline:\n \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n ghost: \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 [a&]:hover:underline\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot.Root : \"span\"\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { Badge, badgeVariants }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card\"\n className={cn(\n \"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\n \"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-title\"\n className={cn(\"leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-action\"\n className={cn(\n \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-content\"\n className={cn(\"px-6\", className)}\n {...props}\n />\n )\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\"flex items-center px-6 [.border-t]:pt-6\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n}\n","import { formatDuration, formatTimecode, usToSec } from '@cut-workbench/core'\n\nexport { formatDuration, formatTimecode }\n\n/** 字节数 → 人类可读 */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n const units = ['KB', 'MB', 'GB']\n let value = bytes / 1024\n let i = 0\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024\n i += 1\n }\n return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[i]}`\n}\n\n/** 像素尺寸 → `1920×1080` */\nexport function formatResolution(width: number, height: number): string {\n if (!width || !height) return '—'\n return `${width}×${height}`\n}\n\n/** 秒数 → `12.3s` */\nexport function formatElapsed(seconds: number): string {\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const m = Math.floor(seconds / 60)\n const s = Math.round(seconds % 60)\n return `${m}m ${s}s`\n}\n\n/** 微秒 → 秒(保留一位小数,用于表单展示) */\nexport function formatSeconds(us: number): string {\n return usToSec(us).toFixed(2)\n}\n\n/** 百分比 0~1 → `42%` */\nexport function formatPercent(ratio: number): string {\n return `${Math.round(ratio * 100)}%`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,SAAgB,GAAG,GAAG,QAAsB;CAC1C,QAAA,GAAOA,eAAAA,QAAAA,EAAAA,GAAQ,KAAA,KAAA,CAAK,MAAM,CAAC;AAC7B;;ACHA,IAAM,iBAAA,GAAgBC,yBAAAA,IAAAA,CACpB,uaACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,WACE;EACF,aACE;EACF,SACE;EACF,OAAO;EACP,MAAM;CACR,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CACF;AAEA,SAAS,MAAM,EACb,WACA,UAAU,WACV,UAAU,OACV,GAAG,SAEyD;CAC5D,MAAM,OAAO,UAAUC,SAAAA,KAAK,OAAO;CAEnC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;EACE,aAAU;EACV,gBAAc;EACd,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS;EACnD,GAAI;CACL,CAAA;AAEL;;ACzCA,SAAS,KAAK,EAAE,WAAW,GAAG,SAAsC;CAClE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,qFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,SAAsC;CACxE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,4JACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,SAAsC;CACvE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,8BAA8B,SAAS;EACrD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAsC;CAC7E,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL;AAeA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAsC;CACzE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,QAAQ,SAAS;EAC/B,GAAI;CACL,CAAA;AAEL;;;ACjEA,SAAgB,YAAY,OAAuB;CACjD,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,MAAM,QAAQ;EAAC;EAAM;EAAM;CAAI;CAC/B,IAAI,QAAQ,QAAQ;CACpB,IAAI,IAAI;CACR,OAAO,SAAS,QAAQ,IAAI,MAAM,SAAS,GAAG;EAC5C,SAAS;EACT,KAAK;CACP;CACA,OAAO,GAAG,MAAM,QAAQ,SAAS,MAAM,IAAI,CAAC,EAAE,GAAG,MAAM;AACzD;;AAGA,SAAgB,iBAAiB,OAAe,QAAwB;CACtE,IAAI,CAAC,SAAS,CAAC,QAAQ,OAAO;CAC9B,OAAO,GAAG,MAAM,GAAG;AACrB;;AAGA,SAAgB,cAAc,SAAyB;CACrD,IAAI,UAAU,IAAI,OAAO,GAAG,QAAQ,QAAQ,CAAC,EAAE;CAG/C,OAAO,GAFG,KAAK,MAAM,UAAU,EAErB,EAAE,IADF,KAAK,MAAM,UAAU,EACf,EAAE;AACpB;;AAGA,SAAgB,cAAc,IAAoB;CAChD,QAAA,GAAOC,oBAAAA,QAAAA,CAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;AAC9B;;AAGA,SAAgB,cAAc,OAAuB;CACnD,OAAO,GAAG,KAAK,MAAM,QAAQ,GAAG,EAAE;AACpC"}
@@ -0,0 +1,109 @@
1
+ import "react";
2
+ import { formatDuration, usToSec } from "@cut-workbench/core";
3
+ import { clsx } from "clsx";
4
+ import { twMerge } from "tailwind-merge";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { cva } from "class-variance-authority";
7
+ import { Slot } from "radix-ui";
8
+ // src/lib/cn.ts
9
+ /**
10
+ * 合并 className:tailwind-merge 负责消解冲突,clsx 负责条件拼接。
11
+ */
12
+ function cn(...inputs) {
13
+ return twMerge(clsx(inputs));
14
+ }
15
+ // src/components/ui/badge.tsx
16
+ var badgeVariants = cva("inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", {
17
+ variants: { variant: {
18
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
19
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
20
+ destructive: "bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
21
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
22
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
23
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
24
+ } },
25
+ defaultVariants: { variant: "default" }
26
+ });
27
+ function Badge({ className, variant = "default", asChild = false, ...props }) {
28
+ const Comp = asChild ? Slot.Root : "span";
29
+ return /* @__PURE__ */ jsx(Comp, {
30
+ "data-slot": "badge",
31
+ "data-variant": variant,
32
+ className: cn(badgeVariants({ variant }), className),
33
+ ...props
34
+ });
35
+ }
36
+ // src/components/ui/card.tsx
37
+ function Card({ className, ...props }) {
38
+ return /* @__PURE__ */ jsx("div", {
39
+ "data-slot": "card",
40
+ className: cn("flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm", className),
41
+ ...props
42
+ });
43
+ }
44
+ function CardHeader({ className, ...props }) {
45
+ return /* @__PURE__ */ jsx("div", {
46
+ "data-slot": "card-header",
47
+ className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className),
48
+ ...props
49
+ });
50
+ }
51
+ function CardTitle({ className, ...props }) {
52
+ return /* @__PURE__ */ jsx("div", {
53
+ "data-slot": "card-title",
54
+ className: cn("leading-none font-semibold", className),
55
+ ...props
56
+ });
57
+ }
58
+ function CardDescription({ className, ...props }) {
59
+ return /* @__PURE__ */ jsx("div", {
60
+ "data-slot": "card-description",
61
+ className: cn("text-sm text-muted-foreground", className),
62
+ ...props
63
+ });
64
+ }
65
+ function CardContent({ className, ...props }) {
66
+ return /* @__PURE__ */ jsx("div", {
67
+ "data-slot": "card-content",
68
+ className: cn("px-6", className),
69
+ ...props
70
+ });
71
+ }
72
+ // src/lib/format.ts
73
+ /** 字节数 → 人类可读 */
74
+ function formatBytes(bytes) {
75
+ if (bytes < 1024) return `${bytes} B`;
76
+ const units = [
77
+ "KB",
78
+ "MB",
79
+ "GB"
80
+ ];
81
+ let value = bytes / 1024;
82
+ let i = 0;
83
+ while (value >= 1024 && i < units.length - 1) {
84
+ value /= 1024;
85
+ i += 1;
86
+ }
87
+ return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[i]}`;
88
+ }
89
+ /** 像素尺寸 → `1920×1080` */
90
+ function formatResolution(width, height) {
91
+ if (!width || !height) return "—";
92
+ return `${width}×${height}`;
93
+ }
94
+ /** 秒数 → `12.3s` */
95
+ function formatElapsed(seconds) {
96
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
97
+ return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
98
+ }
99
+ /** 微秒 → 秒(保留一位小数,用于表单展示) */
100
+ function formatSeconds(us) {
101
+ return usToSec(us).toFixed(2);
102
+ }
103
+ /** 百分比 0~1 → `42%` */
104
+ function formatPercent(ratio) {
105
+ return `${Math.round(ratio * 100)}%`;
106
+ }
107
+ export { formatResolution as a, CardContent as c, CardTitle as d, Badge as f, formatPercent as i, CardDescription as l, formatDuration as n, formatSeconds as o, cn as p, formatElapsed as r, Card as s, formatBytes as t, CardHeader as u };
108
+
109
+ //# sourceMappingURL=format-CV2aoZZC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-CV2aoZZC.js","names":[],"sources":["../src/lib/cn.ts","../src/components/ui/badge.tsx","../src/components/ui/card.tsx","../src/lib/format.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/**\n * 合并 className:tailwind-merge 负责消解冲突,clsx 负责条件拼接。\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Slot } from \"radix-ui\"\n\nconst badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n secondary:\n \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n outline:\n \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n ghost: \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 [a&]:hover:underline\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot.Root : \"span\"\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { Badge, badgeVariants }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card\"\n className={cn(\n \"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\n \"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-title\"\n className={cn(\"leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-action\"\n className={cn(\n \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-content\"\n className={cn(\"px-6\", className)}\n {...props}\n />\n )\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\"flex items-center px-6 [.border-t]:pt-6\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n}\n","import { formatDuration, formatTimecode, usToSec } from '@cut-workbench/core'\n\nexport { formatDuration, formatTimecode }\n\n/** 字节数 → 人类可读 */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n const units = ['KB', 'MB', 'GB']\n let value = bytes / 1024\n let i = 0\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024\n i += 1\n }\n return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[i]}`\n}\n\n/** 像素尺寸 → `1920×1080` */\nexport function formatResolution(width: number, height: number): string {\n if (!width || !height) return '—'\n return `${width}×${height}`\n}\n\n/** 秒数 → `12.3s` */\nexport function formatElapsed(seconds: number): string {\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const m = Math.floor(seconds / 60)\n const s = Math.round(seconds % 60)\n return `${m}m ${s}s`\n}\n\n/** 微秒 → 秒(保留一位小数,用于表单展示) */\nexport function formatSeconds(us: number): string {\n return usToSec(us).toFixed(2)\n}\n\n/** 百分比 0~1 → `42%` */\nexport function formatPercent(ratio: number): string {\n return `${Math.round(ratio * 100)}%`\n}\n"],"mappings":";;;;;;;;;;;AAMA,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;ACHA,IAAM,gBAAgB,IACpB,uaACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,WACE;EACF,aACE;EACF,SACE;EACF,OAAO;EACP,MAAM;CACR,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CACF;AAEA,SAAS,MAAM,EACb,WACA,UAAU,WACV,UAAU,OACV,GAAG,SAEyD;CAC5D,MAAM,OAAO,UAAU,KAAK,OAAO;CAEnC,OACE,oBAAC,MAAD;EACE,aAAU;EACV,gBAAc;EACd,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS;EACnD,GAAI;CACL,CAAA;AAEL;;ACzCA,SAAS,KAAK,EAAE,WAAW,GAAG,SAAsC;CAClE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,qFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,SAAsC;CACxE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,4JACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,SAAsC;CACvE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,8BAA8B,SAAS;EACrD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAsC;CAC7E,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL;AAeA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAsC;CACzE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,QAAQ,SAAS;EAC/B,GAAI;CACL,CAAA;AAEL;;;ACjEA,SAAgB,YAAY,OAAuB;CACjD,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,MAAM,QAAQ;EAAC;EAAM;EAAM;CAAI;CAC/B,IAAI,QAAQ,QAAQ;CACpB,IAAI,IAAI;CACR,OAAO,SAAS,QAAQ,IAAI,MAAM,SAAS,GAAG;EAC5C,SAAS;EACT,KAAK;CACP;CACA,OAAO,GAAG,MAAM,QAAQ,SAAS,MAAM,IAAI,CAAC,EAAE,GAAG,MAAM;AACzD;;AAGA,SAAgB,iBAAiB,OAAe,QAAwB;CACtE,IAAI,CAAC,SAAS,CAAC,QAAQ,OAAO;CAC9B,OAAO,GAAG,MAAM,GAAG;AACrB;;AAGA,SAAgB,cAAc,SAAyB;CACrD,IAAI,UAAU,IAAI,OAAO,GAAG,QAAQ,QAAQ,CAAC,EAAE;CAG/C,OAAO,GAFG,KAAK,MAAM,UAAU,EAErB,EAAE,IADF,KAAK,MAAM,UAAU,EACf,EAAE;AACpB;;AAGA,SAAgB,cAAc,IAAoB;CAChD,OAAO,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;AAC9B;;AAGA,SAAgB,cAAc,OAAuB;CACnD,OAAO,GAAG,KAAK,MAAM,QAAQ,GAAG,EAAE;AACpC"}