Skip to content

Commit daede9c

Browse files
committed
wip
1 parent 19b3344 commit daede9c

File tree

21 files changed

+161
-142
lines changed

21 files changed

+161
-142
lines changed

apps/builder/app/builder/shared/asset-manager/asset-filters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Select } from "@webstudio-is/design-system";
2-
import { FILE_EXTENSIONS_BY_CATEGORY } from "@webstudio-is/asset-uploader";
2+
import { FILE_EXTENSIONS_BY_CATEGORY } from "@webstudio-is/sdk";
33
import { titleCase } from "title-case";
44

55
type FormatCategory = keyof typeof FILE_EXTENSIONS_BY_CATEGORY;

apps/builder/app/builder/shared/asset-manager/asset-info.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from "react";
33
import { useDebouncedCallback } from "use-debounce";
44
import prettyBytes from "pretty-bytes";
55
import { useStore } from "@nanostores/react";
6-
import { getMimeByExtension } from "@webstudio-is/asset-uploader";
6+
import { getMimeTypeByExtension } from "@webstudio-is/sdk";
77
import {
88
Box,
99
Button,
@@ -32,6 +32,7 @@ import {
3232
import {
3333
AspectRatioIcon,
3434
CloudIcon,
35+
CopyIcon,
3536
DimensionsIcon,
3637
DownloadIcon,
3738
GearIcon,
@@ -71,8 +72,10 @@ import {
7172
import {
7273
formatAssetName,
7374
parseAssetName,
75+
getAssetUrl,
7476
} from "~/builder/shared/assets/asset-utils";
7577
import { getFormattedAspectRatio } from "./utils";
78+
import { CopyToClipboard } from "~/shared/copy-to-clipboard";
7679

7780
const buttonLinkClass = css({
7881
all: "unset",
@@ -341,7 +344,7 @@ const AssetInfoContent = ({
341344
<Flex align="center" css={{ gap: theme.spacing[3] }}>
342345
<PageIcon />
343346
<Text variant="labelsSentenceCase">
344-
{getMimeByExtension(`.${ext}`)}
347+
{getMimeTypeByExtension(ext) ?? "unknown"}
345348
</Text>
346349
</Flex>
347350
{"width" in meta && "height" in meta && (
@@ -414,6 +417,22 @@ const AssetInfoContent = ({
414417
/>
415418
</Grid>
416419

420+
<Grid css={{ padding: theme.panel.padding, gap: 4 }}>
421+
<Label htmlFor="asset-manager-id">ID</Label>
422+
<InputField
423+
id="asset-manager-id"
424+
readOnly
425+
value={id}
426+
suffix={
427+
<Flex justify="center" css={{ paddingInline: theme.spacing[2] }}>
428+
<CopyToClipboard text={id}>
429+
<SmallIconButton icon={<CopyIcon />} />
430+
</CopyToClipboard>
431+
</Flex>
432+
}
433+
/>
434+
</Grid>
435+
417436
<Flex justify="between" css={{ padding: theme.panel.padding }}>
418437
{authPermit === "view" ? (
419438
<Tooltip side="bottom" content="View mode. You can't delete assets.">
@@ -470,11 +489,7 @@ const AssetInfoContent = ({
470489
<IconButton
471490
as="a"
472491
download={formatAssetName(asset)}
473-
href={
474-
asset.type === "image"
475-
? `/cgi/image/${asset.name}?format=raw`
476-
: `/cgi/asset/${asset.name}`
477-
}
492+
href={getAssetUrl(asset).href}
478493
>
479494
<DownloadIcon />
480495
</IconButton>

apps/builder/app/builder/shared/asset-manager/asset-manager.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
ALLOWED_FILE_TYPES,
55
IMAGE_EXTENSIONS,
66
VIDEO_EXTENSIONS,
7-
} from "@webstudio-is/asset-uploader";
7+
} from "@webstudio-is/sdk";
88
import type { Asset } from "@webstudio-is/sdk";
99
import { $assets } from "~/shared/nano-states";
1010
import { useEffect } from "react";

apps/builder/app/builder/shared/asset-manager/asset-manager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
useSearchFieldKeys,
88
} from "@webstudio-is/design-system";
99
import type { Asset } from "@webstudio-is/sdk";
10+
import { FILE_EXTENSIONS_BY_CATEGORY } from "@webstudio-is/sdk";
1011
import {
1112
acceptToMimePatterns,
1213
doesAssetMatchMimePatterns,
13-
FILE_EXTENSIONS_BY_CATEGORY,
1414
} from "@webstudio-is/asset-uploader";
1515
import { AssetsShell, type AssetContainer, useAssets } from "../assets";
1616
import { AssetThumbnail } from "./asset-thumbnail";

apps/builder/app/builder/shared/asset-manager/asset-thumbnail.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ import {
1515
import type { IconComponent } from "@webstudio-is/icons";
1616
import {
1717
FILE_EXTENSIONS_BY_CATEGORY,
18-
IMAGE_EXTENSIONS,
19-
isVideoFormat,
20-
} from "@webstudio-is/asset-uploader";
21-
import type { FileCategory } from "@webstudio-is/asset-uploader";
18+
detectAssetType,
19+
} from "@webstudio-is/sdk";
20+
import type { FileCategory } from "@webstudio-is/sdk";
2221

2322
const FORMAT_CATEGORIES = FILE_EXTENSIONS_BY_CATEGORY;
2423

@@ -27,10 +26,6 @@ const CATEGORY_ICON_MAP: Partial<Record<FileCategory, IconComponent>> = {
2726
documents: PageIcon,
2827
};
2928

30-
const isImageFormat = (format: string): boolean => {
31-
return IMAGE_EXTENSIONS.includes(format.toLowerCase());
32-
};
33-
3429
const getFileIcon = (format: string): IconComponent => {
3530
const lowerFormat = format.toLowerCase();
3631

@@ -187,6 +182,7 @@ export const AssetThumbnail = ({
187182
const { basename, ext } = parseAssetName(asset.name);
188183
const alt = asset.description ?? formatAssetName(asset);
189184
const isUploading = assetContainer.status === "uploading";
185+
const assetType = detectAssetType(asset.name);
190186

191187
return (
192188
<ThumbnailContainer
@@ -214,7 +210,7 @@ export const AssetThumbnail = ({
214210
onChange?.(assetContainer);
215211
}}
216212
>
217-
{isImageFormat(asset.format) ? (
213+
{assetType === "image" ? (
218214
// Image files - show preview
219215
<StyledWebstudioImage
220216
assetId={asset.id}
@@ -228,7 +224,7 @@ export const AssetThumbnail = ({
228224
// width={64} used for Image optimizations it should be approximately equal to the width of the picture on the screen in px
229225
width={64}
230226
/>
231-
) : isVideoFormat(asset.format) ? (
227+
) : assetType === "video" ? (
232228
// Video files - show video thumbnail (first frame)
233229
<StyledWebstudioVideo
234230
src={

apps/builder/app/builder/shared/assets/asset-upload.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@ import { type ChangeEvent, useRef } from "react";
22
import { useStore } from "@nanostores/react";
33
import { Button, Flex, Tooltip, toast } from "@webstudio-is/design-system";
44
import { UploadIcon } from "@webstudio-is/icons";
5-
import {
6-
type AssetType,
7-
MAX_UPLOAD_SIZE,
8-
toBytes,
9-
IMAGE_MIME_TYPES,
10-
detectAssetType,
11-
} from "@webstudio-is/asset-uploader";
5+
import { IMAGE_MIME_TYPES, detectAssetType } from "@webstudio-is/sdk";
6+
import { MAX_UPLOAD_SIZE, toBytes } from "@webstudio-is/asset-uploader";
7+
import type { AssetType } from "@webstudio-is/asset-uploader";
128
import { FONT_MIME_TYPES } from "@webstudio-is/fonts";
139
import { uploadAssets } from "./use-assets";
1410
import { $authPermit } from "~/shared/nano-states";
@@ -39,7 +35,7 @@ const useUpload = () => {
3935
// Group files by their detected type
4036
const filesByType = new Map<AssetType, File[]>();
4137
for (const file of files) {
42-
const detectedType = detectAssetType(file);
38+
const detectedType = detectAssetType(file.name);
4339
if (!filesByType.has(detectedType)) {
4440
filesByType.set(detectedType, []);
4541
}
@@ -60,6 +56,7 @@ const useUpload = () => {
6056
const acceptMap = {
6157
image: IMAGE_MIME_TYPES.join(", "),
6258
font: FONT_MIME_TYPES,
59+
video: undefined, // Videos can be uploaded through file type
6360
// We allow everything by default for files, specific validation happens serverside
6461
file: undefined,
6562
};

apps/builder/app/builder/shared/assets/asset-utils.test.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import {
44
formatAssetName,
55
getImageNameAndType,
66
getSha256Hash,
7+
detectAssetType,
78
} from "./asset-utils";
8-
import { detectAssetType } from "@webstudio-is/asset-uploader";
99
import type { Asset } from "@webstudio-is/sdk";
1010

1111
describe("parseAssetName", () => {
@@ -144,41 +144,49 @@ describe("getSha256Hash", () => {
144144

145145
describe("detectAssetType", () => {
146146
test("detects image files", () => {
147-
expect(detectAssetType(new File([], "photo.jpg"))).toBe("image");
148-
expect(detectAssetType(new File([], "image.png"))).toBe("image");
149-
expect(detectAssetType(new File([], "graphic.gif"))).toBe("image");
150-
expect(detectAssetType(new File([], "vector.svg"))).toBe("image");
151-
expect(detectAssetType(new File([], "picture.webp"))).toBe("image");
147+
expect(detectAssetType("photo.jpg")).toBe("image");
148+
expect(detectAssetType("image.png")).toBe("image");
149+
expect(detectAssetType("graphic.gif")).toBe("image");
150+
expect(detectAssetType("vector.svg")).toBe("image");
151+
expect(detectAssetType("picture.webp")).toBe("image");
152152
});
153153

154154
test("detects font files", () => {
155-
expect(detectAssetType(new File([], "font.woff"))).toBe("font");
156-
expect(detectAssetType(new File([], "font.woff2"))).toBe("font");
157-
expect(detectAssetType(new File([], "font.ttf"))).toBe("font");
158-
expect(detectAssetType(new File([], "font.otf"))).toBe("font");
155+
expect(detectAssetType("font.woff")).toBe("font");
156+
expect(detectAssetType("font.woff2")).toBe("font");
157+
expect(detectAssetType("font.ttf")).toBe("font");
158+
expect(detectAssetType("font.otf")).toBe("font");
159+
});
160+
161+
test("detects video files", () => {
162+
expect(detectAssetType("video.mp4")).toBe("video");
163+
expect(detectAssetType("video.webm")).toBe("video");
164+
expect(detectAssetType("video.mov")).toBe("video");
165+
expect(detectAssetType("video.avi")).toBe("video");
159166
});
160167

161168
test("returns file for other types", () => {
162-
expect(detectAssetType(new File([], "document.pdf"))).toBe("file");
163-
expect(detectAssetType(new File([], "video.mp4"))).toBe("file");
164-
expect(detectAssetType(new File([], "audio.mp3"))).toBe("file");
165-
expect(detectAssetType(new File([], "data.json"))).toBe("file");
166-
expect(detectAssetType(new File([], "doc.docx"))).toBe("file");
169+
expect(detectAssetType("document.pdf")).toBe("file");
170+
expect(detectAssetType("audio.mp3")).toBe("file");
171+
expect(detectAssetType("data.json")).toBe("file");
172+
expect(detectAssetType("doc.docx")).toBe("file");
167173
});
168174

169175
test("is case-insensitive", () => {
170-
expect(detectAssetType(new File([], "PHOTO.JPG"))).toBe("image");
171-
expect(detectAssetType(new File([], "FONT.WOFF2"))).toBe("font");
172-
expect(detectAssetType(new File([], "DOC.PDF"))).toBe("file");
176+
expect(detectAssetType("PHOTO.JPG")).toBe("image");
177+
expect(detectAssetType("FONT.WOFF2")).toBe("font");
178+
expect(detectAssetType("VIDEO.MP4")).toBe("video");
179+
expect(detectAssetType("DOC.PDF")).toBe("file");
173180
});
174181

175182
test("handles files without extension", () => {
176-
expect(detectAssetType(new File([], "filename"))).toBe("file");
183+
expect(detectAssetType("filename")).toBe("file");
177184
});
178185

179186
test("handles files with multiple dots", () => {
180-
expect(detectAssetType(new File([], "my.photo.file.png"))).toBe("image");
181-
expect(detectAssetType(new File([], "my.font.file.woff2"))).toBe("font");
182-
expect(detectAssetType(new File([], "my.doc.file.pdf"))).toBe("file");
187+
expect(detectAssetType("my.photo.file.png")).toBe("image");
188+
expect(detectAssetType("my.font.file.woff2")).toBe("font");
189+
expect(detectAssetType("my.video.file.mp4")).toBe("video");
190+
expect(detectAssetType("my.doc.file.pdf")).toBe("file");
183191
});
184192
});

apps/builder/app/builder/shared/assets/asset-utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { nanoid } from "nanoid";
33
import {
44
getMimeTypeByExtension,
55
IMAGE_EXTENSIONS,
6-
} from "@webstudio-is/asset-uploader";
6+
detectAssetType,
7+
getAssetUrl,
8+
} from "@webstudio-is/sdk";
79
import type { UploadingFileData } from "~/shared/nano-states";
810

9-
export { detectAssetType } from "@webstudio-is/asset-uploader";
11+
export { detectAssetType, getAssetUrl };
1012

1113
export const getImageNameAndType = (fileName: string) => {
1214
// Extract extension from filename

apps/builder/app/builder/shared/assets/assets-shell.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
theme,
1515
} from "@webstudio-is/design-system";
1616
import { acceptUploadType, validateFiles } from "./asset-upload";
17-
import { detectAssetType } from "@webstudio-is/asset-uploader";
17+
import { detectAssetType } from "@webstudio-is/sdk";
1818
import { NotFound } from "./not-found";
1919
import { Separator } from "./separator";
2020
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
@@ -152,7 +152,7 @@ export const AssetsShell = ({
152152
// Group files by their detected type
153153
const filesByType = new Map<string, File[]>();
154154
for (const file of files) {
155-
const detectedType = detectAssetType(file);
155+
const detectedType = detectAssetType(file.name);
156156
if (!filesByType.has(detectedType)) {
157157
filesByType.set(detectedType, []);
158158
}

apps/builder/app/routes/cgi.asset.$.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ import { createReadStream, existsSync } from "node:fs";
22
import { join } from "node:path";
33
import { createReadableStreamFromReadable } from "@remix-run/node";
44
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
5-
import {
6-
getMimeTypeByFilename,
7-
isAllowedExtension,
8-
} from "@webstudio-is/asset-uploader";
5+
import { getMimeTypeByFilename, isAllowedExtension } from "@webstudio-is/sdk";
96
import env from "~/env/env.server";
107
import { fileUploadPath } from "~/shared/asset-client";
118

0 commit comments

Comments
 (0)