@burdenoff/microfe-files 2026.625.2 → 2026.625.4
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/files/components/EmptyState.js +31 -34
- package/dist/files/components/EmptyState.js.map +1 -1
- package/dist/files/components/FileBrowser.js +49 -43
- package/dist/files/components/FileBrowser.js.map +1 -1
- package/dist/files/components/TabBar.js.map +1 -1
- package/dist/files/components/UploadQueue.js +27 -20
- package/dist/files/components/UploadQueue.js.map +1 -1
- package/dist/files/pages/BrowserPage.js +310 -275
- package/dist/files/pages/BrowserPage.js.map +1 -1
- package/dist/files/pages/ShareAccessPage.js +42 -42
- package/dist/files/pages/ShareAccessPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,8 +5,9 @@ import { useEffect as n, useRef as r } from "react";
|
|
|
5
5
|
import { Image as i, Pause as a, Play as o, RotateCcw as s, X as c } from "lucide-react";
|
|
6
6
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
7
7
|
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { GlassCard as f } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/files/components/UploadQueue.tsx
|
|
9
|
-
function
|
|
10
|
+
function p({ item: f, onPause: p, onResume: m, onRetry: h, onCancel: g }) {
|
|
10
11
|
let { t: _ } = d(), v = r(null), y = e(f.file.type);
|
|
11
12
|
return n(() => {
|
|
12
13
|
v.current && (v.current.style.width = `${f.progress}%`);
|
|
@@ -113,40 +114,46 @@ function f({ item: f, onPause: p, onResume: m, onRetry: h, onCancel: g }) {
|
|
|
113
114
|
})
|
|
114
115
|
});
|
|
115
116
|
}
|
|
116
|
-
function
|
|
117
|
-
let { t:
|
|
117
|
+
function m({ items: e, isOpen: n, onClose: r, onPause: i, onResume: a, onRetry: o, onCancel: s }) {
|
|
118
|
+
let { t: m } = d();
|
|
118
119
|
if (!n) return null;
|
|
119
|
-
let
|
|
120
|
-
return /* @__PURE__ */ u(
|
|
121
|
-
|
|
120
|
+
let h = e.filter((e) => e.status === "completed").length, g = e.filter((e) => e.status === "failed").length, _ = e.filter((e) => e.status === "uploading" || e.status === "pending").length;
|
|
121
|
+
return /* @__PURE__ */ u(f, {
|
|
122
|
+
treatment: "glass",
|
|
123
|
+
glow: !0,
|
|
124
|
+
className: "fixed bottom-4 right-4 z-50 w-80 max-w-[90vw] md:w-96",
|
|
122
125
|
children: [/* @__PURE__ */ u("div", {
|
|
123
|
-
className: "flex items-center justify-between p-3 border-b border-border-
|
|
126
|
+
className: "flex items-center justify-between p-3 border-b border-border-seam",
|
|
124
127
|
children: [/* @__PURE__ */ u("div", { children: [
|
|
125
128
|
/* @__PURE__ */ u("h3", {
|
|
126
|
-
className: "text-sm font-semibold text-text-primary",
|
|
129
|
+
className: "flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
127
130
|
children: [
|
|
128
|
-
|
|
131
|
+
_ > 0 && /* @__PURE__ */ l("span", {
|
|
132
|
+
className: "size-2 shrink-0 rounded-full bg-action-primary-bg motion-safe:animate-vc-glow-pulse",
|
|
133
|
+
"aria-hidden": "true"
|
|
134
|
+
}),
|
|
135
|
+
t(m, "files.upload.uploads", "Uploads"),
|
|
129
136
|
" (",
|
|
130
|
-
|
|
137
|
+
_,
|
|
131
138
|
" ",
|
|
132
|
-
t(
|
|
139
|
+
t(m, "files.upload.active", "active"),
|
|
133
140
|
")"
|
|
134
141
|
]
|
|
135
142
|
}),
|
|
136
|
-
|
|
143
|
+
h > 0 && /* @__PURE__ */ u("p", {
|
|
137
144
|
className: "text-xs text-status-success-text",
|
|
138
145
|
children: [
|
|
139
|
-
|
|
146
|
+
h,
|
|
140
147
|
" ",
|
|
141
|
-
t(
|
|
148
|
+
t(m, "files.upload.completed", "completed")
|
|
142
149
|
]
|
|
143
150
|
}),
|
|
144
|
-
|
|
151
|
+
g > 0 && /* @__PURE__ */ u("p", {
|
|
145
152
|
className: "text-xs text-status-error-text",
|
|
146
153
|
children: [
|
|
147
|
-
|
|
154
|
+
g,
|
|
148
155
|
" ",
|
|
149
|
-
t(
|
|
156
|
+
t(m, "files.upload.failed", "failed")
|
|
150
157
|
]
|
|
151
158
|
})
|
|
152
159
|
] }), /* @__PURE__ */ l("button", {
|
|
@@ -159,8 +166,8 @@ function p({ items: e, isOpen: n, onClose: r, onPause: i, onResume: a, onRetry:
|
|
|
159
166
|
className: "max-h-64 overflow-y-auto p-2 space-y-2",
|
|
160
167
|
children: e.length === 0 ? /* @__PURE__ */ l("p", {
|
|
161
168
|
className: "text-sm text-text-secondary text-center py-4",
|
|
162
|
-
children: t(
|
|
163
|
-
}) : e.map((e) => /* @__PURE__ */ l(
|
|
169
|
+
children: t(m, "files.upload.noUploads", "No uploads")
|
|
170
|
+
}) : e.map((e) => /* @__PURE__ */ l(p, {
|
|
164
171
|
item: e,
|
|
165
172
|
onPause: i,
|
|
166
173
|
onResume: a,
|
|
@@ -171,6 +178,6 @@ function p({ items: e, isOpen: n, onClose: r, onPause: i, onResume: a, onRetry:
|
|
|
171
178
|
});
|
|
172
179
|
}
|
|
173
180
|
//#endregion
|
|
174
|
-
export {
|
|
181
|
+
export { m as UploadQueue };
|
|
175
182
|
|
|
176
183
|
//# sourceMappingURL=UploadQueue.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UploadQueue.js","names":[],"sources":["../../../src/files/components/UploadQueue.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport {\n Pause,\n Play,\n RotateCcw,\n X,\n CheckCircle,\n AlertCircle,\n Loader2,\n Image as ImageIcon,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { UploadItem } from \"../types\";\nimport { isImageType } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface UploadProgressBarProps {\n item: UploadItem;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nfunction UploadProgressBar({\n item,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadProgressBarProps) {\n const { t } = useI18n();\n const progressRef = useRef<HTMLDivElement>(null);\n const isImage = isImageType(item.file.type);\n\n useEffect(() => {\n if (progressRef.current) {\n progressRef.current.style.width = `${item.progress}%`;\n }\n }, [item.progress]);\n\n const getStatusIcon = () => {\n switch (item.status) {\n case \"uploading\":\n return (\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n );\n case \"paused\":\n return <Pause className=\"size-4 text-status-warning-text\" />;\n case \"completed\":\n return <CheckCircle className=\"size-4 text-status-success-text\" />;\n case \"failed\":\n return <AlertCircle className=\"size-4 text-status-error-text\" />;\n default:\n return <Loader2 className=\"size-4 animate-spin text-text-tertiary\" />;\n }\n };\n\n const getStatusColor = () => {\n switch (item.status) {\n case \"uploading\":\n return \"bg-action-primary-bg\";\n case \"paused\":\n return \"bg-status-warning-bg\";\n case \"completed\":\n return \"bg-status-success-bg\";\n case \"failed\":\n return \"bg-status-error-bg\";\n default:\n return \"bg-text-tertiary\";\n }\n };\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <div className=\"bg-bg-elevated border border-border-default rounded-lg p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-shrink-0 size-10 bg-bg-sunken rounded flex items-center justify-center overflow-hidden\">\n {item.thumbnailUrl ? (\n <img\n src={item.thumbnailUrl}\n alt={item.file.name}\n className=\"w-full h-full object-cover\"\n />\n ) : isImage ? (\n <ImageIcon className=\"size-5 text-text-tertiary\" />\n ) : (\n <div className=\"w-full h-full bg-bg-accent\" />\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2 mb-1.5\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {item.file.name}\n </span>\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatFileSize(item.file.size)}\n </span>\n </div>\n\n <div className=\"relative h-1.5 bg-bg-sunken rounded-full overflow-hidden\">\n <div\n ref={progressRef}\n className={`absolute inset-y-0 left-0 ${getStatusColor()} transition-all duration-300 ease-out rounded-full`}\n style={{ width: `${item.progress}%` }}\n />\n </div>\n\n <div className=\"flex items-center justify-between mt-1.5\">\n <span className=\"text-xs text-text-secondary\">\n {item.status === \"paused\" &&\n tWithFallback(t, \"files.upload.paused\", \"Paused\")}\n {item.status === \"uploading\" && `${item.progress}%`}\n {item.status === \"completed\" &&\n tWithFallback(t, \"files.upload.completed\", \"Completed\")}\n {item.status === \"failed\" &&\n (item.error ||\n tWithFallback(t, \"files.upload.failed\", \"Failed\"))}\n {item.status === \"pending\" &&\n tWithFallback(t, \"files.upload.pending\", \"Pending...\")}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n {item.status === \"uploading\" && (\n <button\n type=\"button\"\n onClick={() => onPause(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.pause\", \"Pause\")}\n >\n <Pause className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"paused\" && (\n <button\n type=\"button\"\n onClick={() => onResume(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.resume\", \"Resume\")}\n >\n <Play className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"failed\" && (\n <button\n type=\"button\"\n onClick={() => onRetry(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.retry\", \"Retry\")}\n >\n <RotateCcw className=\"size-4\" />\n </button>\n )}\n\n {(item.status === \"pending\" ||\n item.status === \"paused\" ||\n item.status === \"failed\") && (\n <button\n type=\"button\"\n onClick={() => onCancel(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.cancel\", \"Cancel\")}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n\ninterface UploadQueueProps {\n items: UploadItem[];\n isOpen: boolean;\n onClose: () => void;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nexport function UploadQueue({\n items,\n isOpen,\n onClose,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadQueueProps) {\n const { t } = useI18n();\n if (!isOpen) return null;\n\n const completedCount = items.filter((i) => i.status === \"completed\").length;\n const failedCount = items.filter((i) => i.status === \"failed\").length;\n const activeCount = items.filter(\n (i) => i.status === \"uploading\" || i.status === \"pending\",\n ).length;\n\n return (\n <div className=\"fixed bottom-4 right-4 w-80 md:w-96 max-w-[90vw] bg-bg-elevated border border-border-default rounded-lg shadow-xl z-50\">\n <div className=\"flex items-center justify-between p-3 border-b border-border-default\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.upload.uploads\", \"Uploads\")} ({activeCount}{\" \"}\n {tWithFallback(t, \"files.upload.active\", \"active\")})\n </h3>\n {completedCount > 0 && (\n <p className=\"text-xs text-status-success-text\">\n {completedCount}{\" \"}\n {tWithFallback(t, \"files.upload.completed\", \"completed\")}\n </p>\n )}\n {failedCount > 0 && (\n <p className=\"text-xs text-status-error-text\">\n {failedCount} {tWithFallback(t, \"files.upload.failed\", \"failed\")}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto p-2 space-y-2\">\n {items.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n {tWithFallback(t, \"files.upload.noUploads\", \"No uploads\")}\n </p>\n ) : (\n items.map((item) => (\n <UploadProgressBar\n key={item.id}\n item={item}\n onPause={onPause}\n onResume={onResume}\n onRetry={onRetry}\n onCancel={onCancel}\n />\n ))\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UploadQueue.js","names":[],"sources":["../../../src/files/components/UploadQueue.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport {\n Pause,\n Play,\n RotateCcw,\n X,\n CheckCircle,\n AlertCircle,\n Loader2,\n Image as ImageIcon,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport type { UploadItem } from \"../types\";\nimport { isImageType } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface UploadProgressBarProps {\n item: UploadItem;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nfunction UploadProgressBar({\n item,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadProgressBarProps) {\n const { t } = useI18n();\n const progressRef = useRef<HTMLDivElement>(null);\n const isImage = isImageType(item.file.type);\n\n useEffect(() => {\n if (progressRef.current) {\n progressRef.current.style.width = `${item.progress}%`;\n }\n }, [item.progress]);\n\n const getStatusIcon = () => {\n switch (item.status) {\n case \"uploading\":\n return (\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n );\n case \"paused\":\n return <Pause className=\"size-4 text-status-warning-text\" />;\n case \"completed\":\n return <CheckCircle className=\"size-4 text-status-success-text\" />;\n case \"failed\":\n return <AlertCircle className=\"size-4 text-status-error-text\" />;\n default:\n return <Loader2 className=\"size-4 animate-spin text-text-tertiary\" />;\n }\n };\n\n const getStatusColor = () => {\n switch (item.status) {\n case \"uploading\":\n return \"bg-action-primary-bg\";\n case \"paused\":\n return \"bg-status-warning-bg\";\n case \"completed\":\n return \"bg-status-success-bg\";\n case \"failed\":\n return \"bg-status-error-bg\";\n default:\n return \"bg-text-tertiary\";\n }\n };\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <div className=\"bg-bg-elevated border border-border-default rounded-lg p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-shrink-0 size-10 bg-bg-sunken rounded flex items-center justify-center overflow-hidden\">\n {item.thumbnailUrl ? (\n <img\n src={item.thumbnailUrl}\n alt={item.file.name}\n className=\"w-full h-full object-cover\"\n />\n ) : isImage ? (\n <ImageIcon className=\"size-5 text-text-tertiary\" />\n ) : (\n <div className=\"w-full h-full bg-bg-accent\" />\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2 mb-1.5\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {item.file.name}\n </span>\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatFileSize(item.file.size)}\n </span>\n </div>\n\n <div className=\"relative h-1.5 bg-bg-sunken rounded-full overflow-hidden\">\n <div\n ref={progressRef}\n className={`absolute inset-y-0 left-0 ${getStatusColor()} transition-all duration-300 ease-out rounded-full`}\n style={{ width: `${item.progress}%` }}\n />\n </div>\n\n <div className=\"flex items-center justify-between mt-1.5\">\n <span className=\"text-xs text-text-secondary\">\n {item.status === \"paused\" &&\n tWithFallback(t, \"files.upload.paused\", \"Paused\")}\n {item.status === \"uploading\" && `${item.progress}%`}\n {item.status === \"completed\" &&\n tWithFallback(t, \"files.upload.completed\", \"Completed\")}\n {item.status === \"failed\" &&\n (item.error ||\n tWithFallback(t, \"files.upload.failed\", \"Failed\"))}\n {item.status === \"pending\" &&\n tWithFallback(t, \"files.upload.pending\", \"Pending...\")}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n {item.status === \"uploading\" && (\n <button\n type=\"button\"\n onClick={() => onPause(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.pause\", \"Pause\")}\n >\n <Pause className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"paused\" && (\n <button\n type=\"button\"\n onClick={() => onResume(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.resume\", \"Resume\")}\n >\n <Play className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"failed\" && (\n <button\n type=\"button\"\n onClick={() => onRetry(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.retry\", \"Retry\")}\n >\n <RotateCcw className=\"size-4\" />\n </button>\n )}\n\n {(item.status === \"pending\" ||\n item.status === \"paused\" ||\n item.status === \"failed\") && (\n <button\n type=\"button\"\n onClick={() => onCancel(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.cancel\", \"Cancel\")}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n\ninterface UploadQueueProps {\n items: UploadItem[];\n isOpen: boolean;\n onClose: () => void;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nexport function UploadQueue({\n items,\n isOpen,\n onClose,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadQueueProps) {\n const { t } = useI18n();\n if (!isOpen) return null;\n\n const completedCount = items.filter((i) => i.status === \"completed\").length;\n const failedCount = items.filter((i) => i.status === \"failed\").length;\n const activeCount = items.filter(\n (i) => i.status === \"uploading\" || i.status === \"pending\",\n ).length;\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow\n className=\"fixed bottom-4 right-4 z-50 w-80 max-w-[90vw] md:w-96\"\n >\n <div className=\"flex items-center justify-between p-3 border-b border-border-seam\">\n <div>\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n {activeCount > 0 && (\n <span\n className=\"size-2 shrink-0 rounded-full bg-action-primary-bg motion-safe:animate-vc-glow-pulse\"\n aria-hidden=\"true\"\n />\n )}\n {tWithFallback(t, \"files.upload.uploads\", \"Uploads\")} ({activeCount}{\" \"}\n {tWithFallback(t, \"files.upload.active\", \"active\")})\n </h3>\n {completedCount > 0 && (\n <p className=\"text-xs text-status-success-text\">\n {completedCount}{\" \"}\n {tWithFallback(t, \"files.upload.completed\", \"completed\")}\n </p>\n )}\n {failedCount > 0 && (\n <p className=\"text-xs text-status-error-text\">\n {failedCount} {tWithFallback(t, \"files.upload.failed\", \"failed\")}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto p-2 space-y-2\">\n {items.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n {tWithFallback(t, \"files.upload.noUploads\", \"No uploads\")}\n </p>\n ) : (\n items.map((item) => (\n <UploadProgressBar\n key={item.id}\n item={item}\n onPause={onPause}\n onResume={onResume}\n onRetry={onRetry}\n onCancel={onCancel}\n />\n ))\n )}\n </div>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
|