From cdbb92358ae7e42809f68658e2cfc8d9f31dd4b3 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 12 Aug 2025 01:50:08 +0200 Subject: [PATCH 1/3] feat: allow user to add asset description Ref https://github.com/webstudio-is/webstudio/issues/3263 Now users can control "alt" for all image uses with asset description. --- .../props-section/props-section.tsx | 1 + .../shared/image-manager/image-info.tsx | 31 ++++++++++++ packages/asset-uploader/package.json | 4 +- .../asset-uploader/src/utils/font-data.ts | 7 --- packages/react-sdk/src/props.ts | 9 ++++ pnpm-lock.yaml | 48 +++++++++---------- 6 files changed, 67 insertions(+), 33 deletions(-) diff --git a/apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx b/apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx index 29b6b07dde00..279df5c509a0 100644 --- a/apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx +++ b/apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx @@ -101,6 +101,7 @@ const renderProperty = ( ) { logic.handleChangeByPropName("width", propValue); logic.handleChangeByPropName("height", propValue); + logic.handleChangeByPropName("alt", propValue); } }, }); diff --git a/apps/builder/app/builder/shared/image-manager/image-info.tsx b/apps/builder/app/builder/shared/image-manager/image-info.tsx index e794c4f44e5f..33864a47da41 100644 --- a/apps/builder/app/builder/shared/image-manager/image-info.tsx +++ b/apps/builder/app/builder/shared/image-manager/image-info.tsx @@ -33,6 +33,7 @@ import { CloudIcon, DimensionsIcon, GearIcon, + InfoCircleIcon, PageIcon, TrashIcon, } from "@webstudio-is/icons"; @@ -287,6 +288,18 @@ const ImageInfoContent = ({ }); } ); + const [description, setDescription] = useLocalValue( + asset.description ?? "", + (newDescription) => { + const assetId = asset.id; + updateWebstudioData((data) => { + const asset = data.assets.get(assetId); + if (asset) { + asset.description = newDescription; + } + }); + } + ); const authPermit = useStore($authPermit); @@ -358,6 +371,24 @@ const ImageInfoContent = ({ + + + setDescription(event.target.value)} + /> + + {authPermit === "view" ? ( diff --git a/packages/asset-uploader/package.json b/packages/asset-uploader/package.json index 971d74e8a0a8..ee52113e2795 100644 --- a/packages/asset-uploader/package.json +++ b/packages/asset-uploader/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", - "@smithy/signature-v4": "^5.0.1", + "@smithy/signature-v4": "^5.1.3", "@webstudio-is/fonts": "workspace:*", "@webstudio-is/sdk": "workspace:*", "@webstudio-is/trpc-interface": "workspace:*", @@ -22,7 +22,7 @@ "warn-once": "^0.1.1" }, "devDependencies": { - "@types/fontkit": "^2.0.7", + "@types/fontkit": "^2.0.8", "@webstudio-is/tsconfig": "workspace:*", "vitest": "^3.1.2", "zod": "^3.24.2" diff --git a/packages/asset-uploader/src/utils/font-data.ts b/packages/asset-uploader/src/utils/font-data.ts index 0dd1f82d32ae..92fbc62552ea 100644 --- a/packages/asset-uploader/src/utils/font-data.ts +++ b/packages/asset-uploader/src/utils/font-data.ts @@ -8,13 +8,6 @@ import { type FontStyle, } from "@webstudio-is/fonts"; -// @todo sumbit this to definitely typed, they are not up to date -declare module "fontkit" { - export interface Font { - variationAxes: VariationAxes; - } -} - // same default fontkit uses internally const defaultLanguage = "en"; diff --git a/packages/react-sdk/src/props.ts b/packages/react-sdk/src/props.ts index bafaf8924eb1..e87e6c795f61 100644 --- a/packages/react-sdk/src/props.ts +++ b/packages/react-sdk/src/props.ts @@ -60,6 +60,15 @@ export const normalizeProps = ({ continue; } + if (prop.name === "alt" && asset.type === "image") { + newProps.push({ + ...propBase, + type: "string", + value: asset.description ?? "", + }); + continue; + } + newProps.push({ ...propBase, type: "string", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 887d18862258..a7ed11a7f6e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1007,8 +1007,8 @@ importers: specifier: ^5.2.0 version: 5.2.0 '@smithy/signature-v4': - specifier: ^5.0.1 - version: 5.0.1 + specifier: ^5.1.3 + version: 5.1.3 '@webstudio-is/fonts': specifier: workspace:* version: link:../fonts @@ -1035,8 +1035,8 @@ importers: version: 0.1.1 devDependencies: '@types/fontkit': - specifier: ^2.0.7 - version: 2.0.7 + specifier: ^2.0.8 + version: 2.0.8 '@webstudio-is/tsconfig': specifier: workspace:* version: link:../tsconfig @@ -5131,20 +5131,20 @@ packages: resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.0.1': - resolution: {integrity: sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==} + '@smithy/protocol-http@5.1.3': + resolution: {integrity: sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.0.1': - resolution: {integrity: sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==} + '@smithy/signature-v4@5.1.3': + resolution: {integrity: sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==} engines: {node: '>=18.0.0'} '@smithy/types@3.7.1': resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} engines: {node: '>=16.0.0'} - '@smithy/types@4.1.0': - resolution: {integrity: sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==} + '@smithy/types@4.3.2': + resolution: {integrity: sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': @@ -5159,8 +5159,8 @@ packages: resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.0.1': - resolution: {integrity: sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==} + '@smithy/util-middleware@4.0.5': + resolution: {integrity: sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.0.0': @@ -5381,8 +5381,8 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - '@types/fontkit@2.0.7': - resolution: {integrity: sha512-f5BjGam6y3FrfEY2JxXwba66SYzqP+FREZh4UuBN1WDePl8EhTKjba3ZZQ2iORUufkrFt/c/UIugj0Uv/HEdRg==} + '@types/fontkit@2.0.8': + resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} '@types/hast@2.3.4': resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} @@ -12390,18 +12390,18 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/protocol-http@5.0.1': + '@smithy/protocol-http@5.1.3': dependencies: - '@smithy/types': 4.1.0 + '@smithy/types': 4.3.2 tslib: 2.8.1 - '@smithy/signature-v4@5.0.1': + '@smithy/signature-v4@5.1.3': dependencies: '@smithy/is-array-buffer': 4.0.0 - '@smithy/protocol-http': 5.0.1 - '@smithy/types': 4.1.0 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 '@smithy/util-hex-encoding': 4.0.0 - '@smithy/util-middleware': 4.0.1 + '@smithy/util-middleware': 4.0.5 '@smithy/util-uri-escape': 4.0.0 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 @@ -12410,7 +12410,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/types@4.1.0': + '@smithy/types@4.3.2': dependencies: tslib: 2.8.1 @@ -12428,9 +12428,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.0.1': + '@smithy/util-middleware@4.0.5': dependencies: - '@smithy/types': 4.1.0 + '@smithy/types': 4.3.2 tslib: 2.8.1 '@smithy/util-uri-escape@4.0.0': @@ -12681,7 +12681,7 @@ snapshots: '@types/estree@1.0.7': {} - '@types/fontkit@2.0.7': + '@types/fontkit@2.0.8': dependencies: '@types/node': 22.13.10 From 4f0cde49559af0006067ce8fe02d556721c5eba6 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 12 Aug 2025 15:41:58 +0200 Subject: [PATCH 2/3] Fix indicator and use review button --- .../app/builder/shared/image-manager/image-info.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/builder/app/builder/shared/image-manager/image-info.tsx b/apps/builder/app/builder/shared/image-manager/image-info.tsx index 33864a47da41..07edea794cdc 100644 --- a/apps/builder/app/builder/shared/image-manager/image-info.tsx +++ b/apps/builder/app/builder/shared/image-manager/image-info.tsx @@ -201,7 +201,7 @@ const AssetUsagesList = ({ usages }: { usages: AssetUsage[] }) => { const UsageDot = styled(Box, { width: 6, height: 6, - backgroundColor: theme.colors.foregroundDestructive, + backgroundColor: "#000", border: "1px solid white", boxShadow: "0 0 3px rgb(0, 0, 0)", borderRadius: "50%", @@ -377,7 +377,10 @@ const ImageInfoContent = ({ css={{ display: "flex", alignItems: "center", gap: 4 }} > Description - + @@ -407,9 +410,7 @@ const ImageInfoContent = ({ ) : ( - + Delete asset? From f96467bd7a132c3b0757d98b871a7ccb63cfe129 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 12 Aug 2025 15:44:49 +0200 Subject: [PATCH 3/3] Remove unnecessary width and height computing --- apps/builder/app/shared/nano-states/props.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apps/builder/app/shared/nano-states/props.ts b/apps/builder/app/shared/nano-states/props.ts index bd9f7a8a11a4..41b52713277c 100644 --- a/apps/builder/app/shared/nano-states/props.ts +++ b/apps/builder/app/shared/nano-states/props.ts @@ -259,20 +259,6 @@ export const $propValuesByInstanceSelector = computed( if (props) { for (const prop of props) { - if (prop.type === "asset" && prop.name === "width") { - const asset = assets.get(prop.value); - if (asset?.type === "image") { - propValues.set("width", asset.meta.width); - } - } - - if (prop.type === "asset" && prop.name === "height") { - const asset = assets.get(prop.value); - if (asset?.type === "image") { - propValues.set("height", asset.meta.height); - } - } - // at this point asset and page either already converted to string // or can be ignored if (prop.type === "asset" || prop.type === "page") {