wc-img-ai 0.3.0 → 0.3.1

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/README.md CHANGED
@@ -41,20 +41,21 @@ pnpm add wc-img-ai
41
41
  | `src` | — | A ready image URL (or data URL). When set, the component acts as a plain `<img>` and never calls the endpoint. Use it when you already have the image. Highest priority. |
42
42
  | `endpoint` | — | Your server route. Receives the POST below. |
43
43
  | `prompt` | — | Description used to generate the image. |
44
- | `image-id` | yes | Storage handle. Provide a known id to fetch a stored image; the server sets it on the element when a new image is minted. |
44
+ | `image-id` | on mint | Storage handle. Provide a known id to fetch a stored image; the server sets it on the element when a new image is minted. |
45
45
  | `llm` | — | Provider/model hint forwarded to the endpoint (e.g. `gemini`, `openai`). |
46
- | `ratio` | — | Aspect ratio forwarded to the endpoint (e.g. `16:9`, `4:1`). |
46
+ | `ratio` | — | Aspect ratio forwarded to the endpoint (e.g. `16:9`, `4:1`). With `width`, it derives the effective height. |
47
47
  | `fallback` | — | Image URL shown if nothing resolves. If omitted, a 1×1 transparent PNG is used. |
48
48
  | `width` | ✅ yes | Intrinsic width (like `<img width>`) — used for the box aspect-ratio and sent to the endpoint. |
49
- | `height` | ✅ yes | Intrinsic height (like `<img height>`). |
49
+ | `height` | | Optional intrinsic height (like `<img height>`); derived when omitted and `width`/`ratio` are set. |
50
50
  | `alt` | ✅ yes | Alt text, passed to the inner `<img>`. |
51
51
 
52
52
  ### Sizing & styling — just like a native `<img>`
53
53
 
54
- Set `width`/`height` and style with `class`/CSS; you rarely need inline
55
- `style`. `width`/`height` become the inner image's content attributes, so the
56
- browser reserves the box from their aspect-ratio (no layout shift) while CSS
57
- controls the displayed size:
54
+ Set `width` plus `ratio`, or the native-image-compatible `width`/`height` pair,
55
+ and style with `class`/CSS; you rarely need inline `style`. The effective
56
+ dimensions become the inner image's content attributes, so the browser reserves
57
+ the box from their aspect-ratio (no layout shift) while CSS controls the
58
+ displayed size:
58
59
 
59
60
  ```html
60
61
  <!-- full-width 3:1 banner, rounded, no layout shift -->
@@ -62,6 +63,15 @@ controls the displayed size:
62
63
  class="block w-full rounded-xl"></ai-img>
63
64
  ```
64
65
 
66
+ When `ratio` and `width` are set, `height` is optional and the component derives
67
+ it. An explicit `height` remains authoritative for callers that need an exact
68
+ output box:
69
+
70
+ ```html
71
+ <ai-img endpoint="/api/img" prompt="…" width="1536" ratio="3:1"
72
+ class="block w-full rounded-xl"></ai-img>
73
+ ```
74
+
65
75
  Visual properties bridge the shadow boundary via `inherit`, so utility classes
66
76
  like `rounded-xl`, `object-cover`, `shadow-lg` on `<ai-img>` style the image.
67
77
  Any other attribute (e.g. `loading`) is passed through to the inner `<img>`.
package/dist/wc-img-ai.js CHANGED
@@ -947,6 +947,19 @@ const RESERVED_ATTRS = /* @__PURE__ */ new Set([
947
947
  const placeholder = (width, height) => "data:image/svg+xml;utf8," + encodeURIComponent(
948
948
  `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"><rect width="${width}" height="${height}" fill="#ddd"/></svg>`
949
949
  );
950
+ const dimensionsFor = (width, height, ratio) => {
951
+ const ratioMatch = ratio.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
952
+ const ratioWidth = Number(ratioMatch == null ? void 0 : ratioMatch[1]);
953
+ const ratioHeight = Number(ratioMatch == null ? void 0 : ratioMatch[2]);
954
+ const numericWidth = Number(width);
955
+ if (!height && Number.isFinite(numericWidth) && numericWidth > 0 && Number.isFinite(ratioWidth) && ratioWidth > 0 && Number.isFinite(ratioHeight) && ratioHeight > 0) {
956
+ return {
957
+ width,
958
+ height: String(Math.round(numericWidth * (ratioHeight / ratioWidth)))
959
+ };
960
+ }
961
+ return { width, height };
962
+ };
950
963
  const _AiImg = class _AiImg extends s$1 {
951
964
  constructor() {
952
965
  super(...arguments);
@@ -987,6 +1000,7 @@ const _AiImg = class _AiImg extends s$1 {
987
1000
  }
988
1001
  start() {
989
1002
  this.collectPassThroughAttributes();
1003
+ const dimensions = dimensionsFor(this.width, this.height, this.ratio);
990
1004
  if (this.src) {
991
1005
  this.resolvedUrl = this.src;
992
1006
  this.settle(this.src);
@@ -996,7 +1010,7 @@ const _AiImg = class _AiImg extends s$1 {
996
1010
  this.settleFallback();
997
1011
  return;
998
1012
  }
999
- this.imgsrc = placeholder(this.width, this.height);
1013
+ this.imgsrc = placeholder(dimensions.width || "1", dimensions.height || "1");
1000
1014
  this.classList.add("spin");
1001
1015
  void this.resolve();
1002
1016
  }
@@ -1008,11 +1022,12 @@ const _AiImg = class _AiImg extends s$1 {
1008
1022
  }
1009
1023
  }
1010
1024
  async resolve() {
1025
+ const dimensions = dimensionsFor(this.width, this.height, this.ratio);
1011
1026
  const result = await resolveImage(this.endpoint, {
1012
1027
  prompt: this.prompt,
1013
1028
  imageId: this.imageId,
1014
- width: Number(this.width) || void 0,
1015
- height: Number(this.height) || void 0,
1029
+ width: Number(dimensions.width) || void 0,
1030
+ height: Number(dimensions.height) || void 0,
1016
1031
  llm: this.llm,
1017
1032
  ratio: this.ratio
1018
1033
  });
@@ -1022,6 +1037,7 @@ const _AiImg = class _AiImg extends s$1 {
1022
1037
  }
1023
1038
  if (result.id && result.id !== this.imageId) {
1024
1039
  this.imageId = result.id;
1040
+ this.setAttribute("image-id", result.id);
1025
1041
  }
1026
1042
  this.dispatchEvent(
1027
1043
  new CustomEvent("ai-image", {
@@ -1044,12 +1060,13 @@ const _AiImg = class _AiImg extends s$1 {
1044
1060
  this.settle(this.fallback || TRANSPARENT_PIXEL);
1045
1061
  }
1046
1062
  render() {
1063
+ const dimensions = dimensionsFor(this.width, this.height, this.ratio);
1047
1064
  return x`
1048
1065
  <img
1049
1066
  src=${this.imgsrc}
1050
1067
  alt=${this.alt}
1051
- width=${this.width || T}
1052
- height=${this.height || T}
1068
+ width=${dimensions.width || T}
1069
+ height=${dimensions.height || T}
1053
1070
  decoding="async"
1054
1071
  @error=${this.onImgError}
1055
1072
  ${spread(this.imgAttributes)}
@@ -1102,7 +1119,7 @@ __decorateClass([
1102
1119
  n$1({ type: String })
1103
1120
  ], AiImg.prototype, "prompt", 2);
1104
1121
  __decorateClass([
1105
- n$1({ type: String, attribute: "image-id", reflect: true })
1122
+ n$1({ type: String, attribute: "image-id" })
1106
1123
  ], AiImg.prototype, "imageId", 2);
1107
1124
  __decorateClass([
1108
1125
  n$1({ type: String })
@@ -1117,7 +1134,7 @@ __decorateClass([
1117
1134
  n$1({ type: String, reflect: true })
1118
1135
  ], AiImg.prototype, "width", 2);
1119
1136
  __decorateClass([
1120
- n$1({ type: String, reflect: true })
1137
+ n$1({ type: String })
1121
1138
  ], AiImg.prototype, "height", 2);
1122
1139
  __decorateClass([
1123
1140
  n$1({ type: String, reflect: true })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wc-img-ai",
3
3
  "private": false,
4
- "version": "0.3.0",
4
+ "version": "0.3.1",
5
5
  "description": "Use AI to generate images for your img tags.",
6
6
  "author": {
7
7
  "name": "John Romani",
package/types/ai-img.d.ts CHANGED
@@ -10,11 +10,11 @@ export declare class AiImg extends LitElement {
10
10
  endpoint: string;
11
11
  /** Description used to generate the image (omit when fetching a known id). */
12
12
  prompt: string;
13
- /** Storage handle. Reflected: set by the server when a new image is minted. */
13
+ /** Storage handle. Reflected after the server mints a new image. */
14
14
  imageId: string;
15
15
  /** Provider/model hint forwarded to the endpoint (e.g. "gemini", "openai"). */
16
16
  llm: string;
17
- /** Aspect ratio forwarded to the endpoint (e.g. "16:9", "4:1"). */
17
+ /** Aspect ratio forwarded to the endpoint and used to derive an omitted height. */
18
18
  ratio: string;
19
19
  /** Shown when the image cannot be resolved (otherwise a 1x1 transparent PNG). */
20
20
  fallback: string;