@feedmepos/mf-menu 0.32.66-dev.2 → 0.32.66-dev.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.
Files changed (83) hide show
  1. package/dist/{App-D0YpNkYr.js → App-DdmEswyc.js} +6 -6
  2. package/dist/{ApplyProduct.vue_vue_type_script_setup_true_lang-YPM2sgEJ.js → ApplyProduct.vue_vue_type_script_setup_true_lang-ChxM7qYy.js} +7 -7
  3. package/dist/{ApplyProductDialog.vue_vue_type_script_setup_true_lang-CAXhUciI.js → ApplyProductDialog.vue_vue_type_script_setup_true_lang-bZiXME9Z.js} +3 -3
  4. package/dist/{Catalog-I3XpPLoT.js → Catalog-GcX5VX2M.js} +6 -6
  5. package/dist/{Category-BTmqDRbK.js → Category-B2Kv3yJp.js} +4 -4
  6. package/dist/{Category-CfCp_M4X.js → Category-BwH-wVv2.js} +4 -4
  7. package/dist/{CookingGuide-BRHwTegP.js → CookingGuide-CWDprGu6.js} +5 -5
  8. package/dist/{CustomAttributeChip.vue_vue_type_script_setup_true_lang-ksv-RMn2.js → CustomAttributeChip.vue_vue_type_script_setup_true_lang-CmACeNnV.js} +1 -1
  9. package/dist/{Group-Bwj6UATr.js → Group-BavmrRXD.js} +3 -3
  10. package/dist/{Group-BvrANEY8.js → Group-YNbaHa27.js} +1 -1
  11. package/dist/{Group.vue_vue_type_script_setup_true_lang-CYLH4YhA.js → Group.vue_vue_type_script_setup_true_lang-9rGR3rec.js} +7 -7
  12. package/dist/{GroupMarkupPriceDialog.vue_vue_type_script_setup_true_lang-DWpeinru.js → GroupMarkupPriceDialog.vue_vue_type_script_setup_true_lang-D12xx6Vd.js} +5 -5
  13. package/dist/{ImportProductDialog.vue_vue_type_script_setup_true_lang-DEvUaDcb.js → ImportProductDialog.vue_vue_type_script_setup_true_lang-NAKJ0sFP.js} +9 -8
  14. package/dist/{Ingredient-B8n9cRsp.js → Ingredient-DHjSUUIP.js} +4 -4
  15. package/dist/{InventoryBinding.vue_vue_type_script_setup_true_lang-92V2JZvW.js → InventoryBinding.vue_vue_type_script_setup_true_lang-U5AkGUri.js} +2 -2
  16. package/dist/{LinkProductSideSheet-LF2pdsYn.js → LinkProductSideSheet-BkNMJOiZ.js} +4 -4
  17. package/dist/{MenuSetting-DrObZATH.js → MenuSetting-Bm4O6MZ6.js} +7 -7
  18. package/dist/{OrderPacing-CIP1oshR.js → OrderPacing-DliNofLl.js} +5 -5
  19. package/dist/{PrintRoute-BUErmYxT.js → PrintRoute-1Py1_tzI.js} +5 -5
  20. package/dist/{Product-DBRc2y1I.js → Product-D3kyQmwQ.js} +1 -1
  21. package/dist/{Product.vue_vue_type_script_setup_true_lang-BvtMo0M4.js → Product.vue_vue_type_script_setup_true_lang-D-heQeQu.js} +622 -610
  22. package/dist/{ProductInternalTools-CnYr5piv.js → ProductInternalTools-87_B55Fr.js} +6 -6
  23. package/dist/{Products-DkqJELto.js → Products-B-J389Ks.js} +52 -52
  24. package/dist/Publish-BlYboQl0.js +1532 -0
  25. package/dist/{Recipe-DxTc4llT.js → Recipe-qb3ZdJGw.js} +5 -5
  26. package/dist/{RestaurantAttributesSelector.vue_vue_type_script_setup_true_lang-BC59l-9q.js → RestaurantAttributesSelector.vue_vue_type_script_setup_true_lang-CXqE4HLg.js} +1 -1
  27. package/dist/{RuleView.vue_vue_type_script_setup_true_lang-BpGitWig.js → RuleView.vue_vue_type_script_setup_true_lang-CODY5qdL.js} +3 -3
  28. package/dist/{Scheduler-CtdYHGoQ.js → Scheduler-CeM3y0Ha.js} +3 -3
  29. package/dist/{ServingSequence-Bx0Z7ob9.js → ServingSequence-BCzXUO9n.js} +124 -121
  30. package/dist/{Setting-BWKT6o_d.js → Setting-sy6vMuPB.js} +5 -5
  31. package/dist/{Subcategory-BckJmrDm.js → Subcategory-BZti9kYo.js} +6 -6
  32. package/dist/{Takeaway-Dvt7GRhi.js → Takeaway-C9puqL8n.js} +4 -4
  33. package/dist/{Takeaway-DULjBrov.js → Takeaway-D5JlmD6u.js} +1 -1
  34. package/dist/{Takeaway.vue_vue_type_script_setup_true_lang-BanPhJ_p.js → Takeaway.vue_vue_type_script_setup_true_lang-j8d0lXOL.js} +10 -10
  35. package/dist/{TranslationFieldSection.vue_vue_type_script_setup_true_lang-BA1GwxTn.js → TranslationFieldSection.vue_vue_type_script_setup_true_lang-DX_DRNF2.js} +1 -1
  36. package/dist/{TranslationSideSheet.vue_vue_type_script_setup_true_lang-B45Qj3ka.js → TranslationSideSheet.vue_vue_type_script_setup_true_lang-DesC2w1k.js} +2 -2
  37. package/dist/{TreeEditorOpenner.vue_vue_type_script_setup_true_lang-mlqLqR9u.js → TreeEditorOpenner.vue_vue_type_script_setup_true_lang-CmuuSoGs.js} +15 -15
  38. package/dist/{Unit-C2rVtZXW.js → Unit-Cc42yyN6.js} +2 -2
  39. package/dist/{Variant-C1aRShIB.js → Variant-D19fCBB3.js} +3 -3
  40. package/dist/{_id_-TNQRaQmS.js → _id_-B9MKg_D-.js} +10 -10
  41. package/dist/{app-Cbzk6cOz.js → app-BN0HgwNB.js} +328 -328
  42. package/dist/{app-DwyLCetV.js → app-kMpCkgGC.js} +1 -1
  43. package/dist/app.js +1 -1
  44. package/dist/apps/mf-menu/src/api/media.d.ts +63 -10
  45. package/dist/apps/mf-menu/src/app.d.ts +130 -10
  46. package/dist/apps/mf-menu/src/components/feature/Fields/RangeSlider.vue.d.ts +62 -0
  47. package/dist/apps/mf-menu/src/components/feature/TreeEditor/composable/productImages.d.ts +95 -12
  48. package/dist/apps/mf-menu/src/components/feature/TreeEditor/image/ImageEditorDialog.vue.d.ts +56 -0
  49. package/dist/apps/mf-menu/src/composable/imageSource.d.ts +7 -0
  50. package/dist/apps/mf-menu/src/composable/imageVersion.d.ts +11 -0
  51. package/dist/apps/mf-menu/src/helper/itemOverrider.d.ts +10 -0
  52. package/dist/apps/mf-menu/src/stores/menu.d.ts +2 -0
  53. package/dist/apps/mf-menu/src/views/Publish/publishStatus.d.ts +27 -0
  54. package/dist/apps/mf-menu/tsconfig.app.tsbuildinfo +1 -1
  55. package/dist/assets/{override-menu-Ccftxk6t.js → linked-status-CfzY6n0e.js} +4 -4
  56. package/dist/assets/{menu-export-BaTSKjZt.js → menu-export-CALDQ8k1.js} +3 -3
  57. package/dist/assets/{linked-status-BY2u9ovw.js → override-menu-CmqrughS.js} +4 -4
  58. package/dist/assets/{validate-menu-CLI_SnXI.js → validate-menu-BgjAdwUE.js} +4 -4
  59. package/dist/{catalog-CVzF_mVG.js → catalog-BuKsorK6.js} +1 -1
  60. package/dist/{catalogSetting-C1g9ndFL.js → catalogSetting-B5EtNHwb.js} +1 -1
  61. package/dist/{currency-BMMqzZYC.js → currency-BTbX6PWL.js} +1 -1
  62. package/dist/{dayjs.min-BozAuX2f.js → dayjs.min-KeFRLajb.js} +2 -2
  63. package/dist/{discardConfirm-CFESnGLS.js → discardConfirm-zfX1nuye.js} +1 -1
  64. package/dist/{index-BNzLcju2.js → index-DDJHOjwR.js} +1 -1
  65. package/dist/{index-CyomkF6Q.js → index-DTakcRjO.js} +5 -5
  66. package/dist/{index-BRt5TwuO.js → index-DfoDRA0k.js} +6 -6
  67. package/dist/{index.vue_vue_type_script_setup_true_lang-CESDAztS.js → index.vue_vue_type_script_setup_true_lang-DybNA4Np.js} +24886 -24717
  68. package/dist/{item-Bg78_1Z3.js → item-jpOmJGLW.js} +5058 -4584
  69. package/dist/{jszip.min-CfmqMq7-.js → jszip.min-DyvQ41lT.js} +3 -3
  70. package/dist/{linkProducts-Dk-7vVTM.js → linkProducts-DFJMpL5n.js} +4 -4
  71. package/dist/{memberPricing-BeLPeWJc.js → memberPricing-BZgeIGmU.js} +1 -1
  72. package/dist/{menu-Cn6vin1M.js → menu-BupVgTY-.js} +1 -1
  73. package/dist/{menuV2-DMl-WMCG.js → menuV2-B9-ODrLW.js} +85 -85
  74. package/dist/mf-menu.css +1 -1
  75. package/dist/{order_pacing-SVJqBJun.js → order_pacing-Dfey1efO.js} +5 -5
  76. package/dist/{priceTier-BOIqyyMG.js → priceTier-eGLFeyfY.js} +1 -1
  77. package/dist/{rules-Bw94jp35.js → rules-BS6BLc8X.js} +3 -3
  78. package/dist/{scheduler-BooRYOi1.js → scheduler-3BGOjjCO.js} +5 -5
  79. package/dist/{toExcel-FAK9N74s.js → toExcel-BmTqFf7v.js} +2 -2
  80. package/dist/{unit-BiH3semx.js → unit-D2oVUlp4.js} +3 -3
  81. package/package.json +1 -1
  82. package/dist/Publish-C94D_B2o.js +0 -1434
  83. package/dist/apps/mf-menu/src/components/feature/TreeEditor/image/ImageUploadDialog.vue.d.ts +0 -18
@@ -1,5 +1,5 @@
1
1
  import { defineStore as r } from "pinia";
2
- import { J as a, K as s, L as l } from "./index.vue_vue_type_script_setup_true_lang-CESDAztS.js";
2
+ import { J as a, K as s, L as l } from "./index.vue_vue_type_script_setup_true_lang-DybNA4Np.js";
3
3
  const u = {
4
4
  async read() {
5
5
  return a(await s().get("/public-setting"));
package/dist/app.js CHANGED
@@ -1,4 +1,4 @@
1
- import { F as e, i as r, j as o, o as t, r as i, u as p } from "./app-Cbzk6cOz.js";
1
+ import { F as e, i as r, j as o, o as t, r as i, u as p } from "./app-BN0HgwNB.js";
2
2
  export {
3
3
  e as FmApp,
4
4
  r as i18nMessages,
@@ -14,6 +14,15 @@ export declare function normalizeMediaRefId(refId: string): string;
14
14
  export type AspectRatio = string;
15
15
  /** Ratios every product image is expected to carry; they cannot be removed in the UI. */
16
16
  export declare const REQUIRED_ASPECT_RATIOS: AspectRatio[];
17
+ /**
18
+ * Reserved ratio word for the uploaded image itself (backend `model.RatioOriginal`).
19
+ *
20
+ * Not a generatable ratio: `/images/create` rejects it, and on `/images/update` it
21
+ * selects the write-the-original mode rather than naming a variant. Kept as a constant
22
+ * because `AspectRatio` is a bare string — the type cannot keep the two apart, so the
23
+ * distinction has to be visible at the call site.
24
+ */
25
+ export declare const RATIO_ORIGINAL = "original";
17
26
  export type VariantStatus = "pending" | "active" | "failed";
18
27
  export interface MediaVariant {
19
28
  aspectRatio: AspectRatio;
@@ -106,18 +115,23 @@ export declare const mediaService: {
106
115
  mode: "append" | "replace";
107
116
  }): Promise<CreateRes>;
108
117
  /**
109
- * Rewrites exactly one aspect ratio. Every other ratio and the original are left
110
- * alone `original` itself is rejected with 400, it is immutable for its lifetime,
111
- * which is what keeps a stored thumbnail URL stable.
118
+ * Regenerates exactly one aspect ratio, leaving every other ratio and the original
119
+ * alone. For rewriting the original itself, see `updateOriginal` same endpoint,
120
+ * different mode, and the only one that is not AI generation.
112
121
  *
113
- * Which of the two modes runs is decided by the field that is filled:
114
- * - `base64` overwrites the variant with the merchant's own bytes, synchronously
115
- * - `instruction` queues an AI re-edit, so the ratio drops back to `pending`
116
- * (with `regenerateFromOrigin`, it re-derives from the original instead of the
117
- * current variant, discarding previous variant edits)
122
+ * Both modes here are AI generation, and which one runs is decided by the field that
123
+ * is filled (either, or both):
124
+ * - `base64` stores the supplied bytes as the *source* and queues generation from
125
+ * them. It is not a passthrough: the service trims uniform bars off the source and
126
+ * hands it to the model, so what lands is a redraw of the merchant's image at this
127
+ * ratio, not the merchant's image. Changed by media-service `db04d1f`; before that
128
+ * it was a synchronous byte write, which is what this client used to document.
129
+ * - `instruction` queues a re-edit of the variant's *current* image (with
130
+ * `regenerateFromOrigin`, of the original instead, discarding previous edits).
118
131
  *
119
- * Callers must send exactly one of `base64` or `instruction`; the service rejects an
120
- * update request that contains neither with `400 INVALID_PARAM`.
132
+ * Either way the ratio drops back to `pending` for ~40s and the request consumes one
133
+ * AI generation from the business's license allowance. The service rejects an update
134
+ * carrying neither field with `400 INVALID_PARAM`.
121
135
  *
122
136
  * Unlike an upload these bytes do pass through the service pod — there is no
123
137
  * variant-level `upload-intent` — so callers must send a cropped, re-encoded image
@@ -139,6 +153,45 @@ export declare const mediaService: {
139
153
  */
140
154
  regenerateFromOrigin?: boolean;
141
155
  }): Promise<void>;
156
+ /**
157
+ * Rewrites the uploaded original in place, synchronously (media-service `a0cc055`).
158
+ *
159
+ * The one mode of `/images/update` that is not AI: the bytes are stored as-is and
160
+ * nothing is queued, so no generation is billed. Sending `instruction`, `params` or
161
+ * `regenerateFromOrigin` alongside `aspectRatio: "original"` is a `400 INVALID_PARAM`
162
+ * — hence a separate method rather than a flag on `update`, so the invalid combination
163
+ * cannot be expressed here at all.
164
+ *
165
+ * `base64` may be a data URL or bare base64; the stored Content-Type is sniffed from
166
+ * the bytes rather than read off the data URL. The service re-reads `width`, `height`,
167
+ * `mimeType` and `fileSize` from them — `fileName` is deliberately left as it was.
168
+ * Limits are the upload's: 10 MiB decoded (`413 PAYLOAD_TOO_LARGE`, checked on the
169
+ * encoded length too), jpeg/png/webp only (`415 UNSUPPORTED_MEDIA_TYPE`).
170
+ *
171
+ * Two things the caller owns afterwards:
172
+ * - **Stale variants.** Existing ratios are retained and are *not* regenerated, so
173
+ * they still derive from the photo that was just replaced. Requeue the ones that
174
+ * should follow the original with `create({ mode: "append" })` — `generateAspectRatios`
175
+ * with `includeExisting` — skipping any ratio whose `imageSet` value is set, since
176
+ * that marks a photo the merchant supplied and must never be regenerated out from
177
+ * under them. It has to be `append`: `replace` also *deletes* every variant absent
178
+ * from the request, so requeueing a subset with it would take the merchant's own
179
+ * ratios down with it.
180
+ * - **Caches.** The URL does not change, which is the point: nothing in the menu
181
+ * document has to be written. The service purges the original URL from the edge, but
182
+ * that is fire-and-forget (and a no-op without Cloudflare credentials) and cannot
183
+ * reach a browser that already fetched it, so surfaces showing the image must bust
184
+ * their own cache with a `?t=` stamp. Safe to add: image-router only rewrites a
185
+ * request that carries `aspect_ratio`. Never persist the stamped URL.
186
+ *
187
+ * The response is dropped for the same reason as `update`'s, and because it carries
188
+ * nothing new: the URL is unchanged and the status is always `active`.
189
+ */
190
+ updateOriginal(req: {
191
+ refId: string;
192
+ imageId: string;
193
+ base64: string;
194
+ }): Promise<void>;
142
195
  listImages(refIds: string[]): Promise<MediaImage[]>;
143
196
  getGenerationTask(taskId: string): Promise<GenerationTask>;
144
197
  delete(entries: DeleteEntry[]): Promise<void>;
@@ -1617,6 +1617,8 @@ export declare const i18nMessages: {
1617
1617
  };
1618
1618
  status: {
1619
1619
  latest: string;
1620
+ publishing: string;
1621
+ queued: string;
1620
1622
  outdated: string;
1621
1623
  };
1622
1624
  };
@@ -1966,6 +1968,29 @@ export declare const i18nMessages: {
1966
1968
  };
1967
1969
  product_image: {
1968
1970
  hint: string;
1971
+ crop_hint: string;
1972
+ editor: {
1973
+ title: string;
1974
+ subtitle_upload: string;
1975
+ subtitle_counter: string;
1976
+ subtitle_edit: string;
1977
+ upload_rest: string;
1978
+ ratio: string;
1979
+ ratio_free: string;
1980
+ zoom: string;
1981
+ rotate: string;
1982
+ reset: string;
1983
+ output_original: string;
1984
+ output_edited: string;
1985
+ oversized: string;
1986
+ edit_notice_title: string;
1987
+ edit_notice_body: string;
1988
+ replace_notice_title: string;
1989
+ replace_notice_body: string;
1990
+ saving: string;
1991
+ saved: string;
1992
+ formats_failed: string;
1993
+ };
1969
1994
  drop_hint: string;
1970
1995
  add_file: string;
1971
1996
  cover_badge: string;
@@ -2021,11 +2046,10 @@ export declare const i18nMessages: {
2021
2046
  replace: {
2022
2047
  title: string;
2023
2048
  action: string;
2024
- unavailable: string;
2025
2049
  hint: string;
2026
2050
  accepts: string;
2027
2051
  no_file: string;
2028
- done: string;
2052
+ queued: string;
2029
2053
  };
2030
2054
  regenerate: {
2031
2055
  title: string;
@@ -3653,6 +3677,8 @@ export declare const i18nMessages: {
3653
3677
  };
3654
3678
  status: {
3655
3679
  latest: string;
3680
+ publishing: string;
3681
+ queued: string;
3656
3682
  outdated: string;
3657
3683
  };
3658
3684
  };
@@ -4000,6 +4026,29 @@ export declare const i18nMessages: {
4000
4026
  };
4001
4027
  product_image: {
4002
4028
  hint: string;
4029
+ crop_hint: string;
4030
+ editor: {
4031
+ title: string;
4032
+ subtitle_upload: string;
4033
+ subtitle_counter: string;
4034
+ subtitle_edit: string;
4035
+ upload_rest: string;
4036
+ ratio: string;
4037
+ ratio_free: string;
4038
+ zoom: string;
4039
+ rotate: string;
4040
+ reset: string;
4041
+ output_original: string;
4042
+ output_edited: string;
4043
+ oversized: string;
4044
+ edit_notice_title: string;
4045
+ edit_notice_body: string;
4046
+ replace_notice_title: string;
4047
+ replace_notice_body: string;
4048
+ saving: string;
4049
+ saved: string;
4050
+ formats_failed: string;
4051
+ };
4003
4052
  drop_hint: string;
4004
4053
  add_file: string;
4005
4054
  cover_badge: string;
@@ -4055,11 +4104,10 @@ export declare const i18nMessages: {
4055
4104
  replace: {
4056
4105
  title: string;
4057
4106
  action: string;
4058
- unavailable: string;
4059
4107
  hint: string;
4060
4108
  accepts: string;
4061
4109
  no_file: string;
4062
- done: string;
4110
+ queued: string;
4063
4111
  };
4064
4112
  regenerate: {
4065
4113
  title: string;
@@ -5687,6 +5735,8 @@ export declare const i18nMessages: {
5687
5735
  };
5688
5736
  status: {
5689
5737
  latest: string;
5738
+ publishing: string;
5739
+ queued: string;
5690
5740
  outdated: string;
5691
5741
  };
5692
5742
  };
@@ -6035,6 +6085,29 @@ export declare const i18nMessages: {
6035
6085
  };
6036
6086
  product_image: {
6037
6087
  hint: string;
6088
+ crop_hint: string;
6089
+ editor: {
6090
+ title: string;
6091
+ subtitle_upload: string;
6092
+ subtitle_counter: string;
6093
+ subtitle_edit: string;
6094
+ upload_rest: string;
6095
+ ratio: string;
6096
+ ratio_free: string;
6097
+ zoom: string;
6098
+ rotate: string;
6099
+ reset: string;
6100
+ output_original: string;
6101
+ output_edited: string;
6102
+ oversized: string;
6103
+ edit_notice_title: string;
6104
+ edit_notice_body: string;
6105
+ replace_notice_title: string;
6106
+ replace_notice_body: string;
6107
+ saving: string;
6108
+ saved: string;
6109
+ formats_failed: string;
6110
+ };
6038
6111
  drop_hint: string;
6039
6112
  add_file: string;
6040
6113
  cover_badge: string;
@@ -6090,11 +6163,10 @@ export declare const i18nMessages: {
6090
6163
  replace: {
6091
6164
  title: string;
6092
6165
  action: string;
6093
- unavailable: string;
6094
6166
  hint: string;
6095
6167
  accepts: string;
6096
6168
  no_file: string;
6097
- done: string;
6169
+ queued: string;
6098
6170
  };
6099
6171
  regenerate: {
6100
6172
  title: string;
@@ -7722,6 +7794,8 @@ export declare const i18nMessages: {
7722
7794
  };
7723
7795
  status: {
7724
7796
  latest: string;
7797
+ publishing: string;
7798
+ queued: string;
7725
7799
  outdated: string;
7726
7800
  };
7727
7801
  };
@@ -8069,6 +8143,29 @@ export declare const i18nMessages: {
8069
8143
  };
8070
8144
  product_image: {
8071
8145
  hint: string;
8146
+ crop_hint: string;
8147
+ editor: {
8148
+ title: string;
8149
+ subtitle_upload: string;
8150
+ subtitle_counter: string;
8151
+ subtitle_edit: string;
8152
+ upload_rest: string;
8153
+ ratio: string;
8154
+ ratio_free: string;
8155
+ zoom: string;
8156
+ rotate: string;
8157
+ reset: string;
8158
+ output_original: string;
8159
+ output_edited: string;
8160
+ oversized: string;
8161
+ edit_notice_title: string;
8162
+ edit_notice_body: string;
8163
+ replace_notice_title: string;
8164
+ replace_notice_body: string;
8165
+ saving: string;
8166
+ saved: string;
8167
+ formats_failed: string;
8168
+ };
8072
8169
  drop_hint: string;
8073
8170
  add_file: string;
8074
8171
  cover_badge: string;
@@ -8124,11 +8221,10 @@ export declare const i18nMessages: {
8124
8221
  replace: {
8125
8222
  title: string;
8126
8223
  action: string;
8127
- unavailable: string;
8128
8224
  hint: string;
8129
8225
  accepts: string;
8130
8226
  no_file: string;
8131
- done: string;
8227
+ queued: string;
8132
8228
  };
8133
8229
  regenerate: {
8134
8230
  title: string;
@@ -9745,6 +9841,8 @@ export declare const i18nMessages: {
9745
9841
  };
9746
9842
  status: {
9747
9843
  latest: string;
9844
+ publishing: string;
9845
+ queued: string;
9748
9846
  outdated: string;
9749
9847
  };
9750
9848
  };
@@ -10092,6 +10190,29 @@ export declare const i18nMessages: {
10092
10190
  };
10093
10191
  product_image: {
10094
10192
  hint: string;
10193
+ crop_hint: string;
10194
+ editor: {
10195
+ title: string;
10196
+ subtitle_upload: string;
10197
+ subtitle_counter: string;
10198
+ subtitle_edit: string;
10199
+ upload_rest: string;
10200
+ ratio: string;
10201
+ ratio_free: string;
10202
+ zoom: string;
10203
+ rotate: string;
10204
+ reset: string;
10205
+ output_original: string;
10206
+ output_edited: string;
10207
+ oversized: string;
10208
+ edit_notice_title: string;
10209
+ edit_notice_body: string;
10210
+ replace_notice_title: string;
10211
+ replace_notice_body: string;
10212
+ saving: string;
10213
+ saved: string;
10214
+ formats_failed: string;
10215
+ };
10095
10216
  drop_hint: string;
10096
10217
  add_file: string;
10097
10218
  cover_badge: string;
@@ -10147,11 +10268,10 @@ export declare const i18nMessages: {
10147
10268
  replace: {
10148
10269
  title: string;
10149
10270
  action: string;
10150
- unavailable: string;
10151
10271
  hint: string;
10152
10272
  accepts: string;
10153
10273
  no_file: string;
10154
- done: string;
10274
+ queued: string;
10155
10275
  };
10156
10276
  regenerate: {
10157
10277
  title: string;
@@ -0,0 +1,62 @@
1
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
2
+ modelValue: number;
3
+ min?: number | undefined;
4
+ max?: number | undefined;
5
+ step?: number | undefined;
6
+ disabled?: boolean | undefined;
7
+ ariaLabel?: string | undefined;
8
+ /** Renders the bubble shown while dragging; no bubble when omitted. */
9
+ formatValue?: ((value: number) => string) | undefined;
10
+ }>, {
11
+ min: number;
12
+ max: number;
13
+ step: number;
14
+ disabled: boolean;
15
+ ariaLabel: undefined;
16
+ formatValue: undefined;
17
+ }>>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
+ "update:modelValue": (value: number) => void;
19
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
20
+ modelValue: number;
21
+ min?: number | undefined;
22
+ max?: number | undefined;
23
+ step?: number | undefined;
24
+ disabled?: boolean | undefined;
25
+ ariaLabel?: string | undefined;
26
+ /** Renders the bubble shown while dragging; no bubble when omitted. */
27
+ formatValue?: ((value: number) => string) | undefined;
28
+ }>, {
29
+ min: number;
30
+ max: number;
31
+ step: number;
32
+ disabled: boolean;
33
+ ariaLabel: undefined;
34
+ formatValue: undefined;
35
+ }>>> & Readonly<{
36
+ "onUpdate:modelValue"?: ((value: number) => any) | undefined;
37
+ }>, {
38
+ disabled: boolean;
39
+ max: number;
40
+ min: number;
41
+ step: number;
42
+ ariaLabel: string;
43
+ formatValue: (value: number) => string;
44
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
45
+ export default _default;
46
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
47
+ type __VLS_TypePropsToRuntimeProps<T> = {
48
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
49
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
50
+ } : {
51
+ type: import('vue').PropType<T[K]>;
52
+ required: true;
53
+ };
54
+ };
55
+ type __VLS_WithDefaults<P, D> = {
56
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
57
+ default: D[K];
58
+ }> : P[K];
59
+ };
60
+ type __VLS_Prettify<T> = {
61
+ [K in keyof T]: T[K];
62
+ } & {};
@@ -43,6 +43,52 @@ export declare function generateImageId(): string;
43
43
  */
44
44
  export declare function autoAspectRatios(autoGenerate: boolean): AspectRatio[];
45
45
  export declare function validateImageFile(file: File): string | null;
46
+ /**
47
+ * The half of `validateImageFile` that is a real dead end.
48
+ *
49
+ * A file the browser cannot decode is refused outright, but being over
50
+ * `MAX_IMAGE_BYTES` is not a reason to refuse a photo any more: the editor re-encodes it
51
+ * under the limit (`canvasToFile`), which is the whole point of opening on upload. Call
52
+ * sites that hand bytes straight to media-service — Remy's output, a staged flush —
53
+ * still use the combined check, because nothing there can shrink a file.
54
+ */
55
+ export declare function validateImageType(file: File): string | null;
56
+ /**
57
+ * Longest edge the editor writes out.
58
+ *
59
+ * Generous — media-service derives every ratio from this original, and 2400px still
60
+ * covers a retina hero on the biggest POS screen — but bounded, so a 48MP phone photo
61
+ * cannot land as a 20 MB original that has to be re-encoded on every later edit.
62
+ */
63
+ export declare const MAX_IMAGE_EDGE = 2400;
64
+ /**
65
+ * Encodes an edited canvas as a file guaranteed to be uploadable.
66
+ *
67
+ * "Guaranteed" is the job: `upload-intent` rejects anything over `MAX_IMAGE_BYTES` with a
68
+ * 413 and `updateOriginal` does the same on the encoded length, so the editor must not be
69
+ * able to hand back a file the service will refuse — that would put the merchant back at
70
+ * the dead end this feature exists to remove. Quality is spent first and pixels second,
71
+ * because a slightly softer photo beats a smaller one.
72
+ *
73
+ * PNG is kept only when the caller asks for it (a source with transparency); it has no
74
+ * quality lever, so it is shrunk by size alone.
75
+ */
76
+ export declare function canvasToFile(canvas: HTMLCanvasElement, options: {
77
+ fileName: string;
78
+ mime: string;
79
+ maxEdge?: number;
80
+ maxBytes?: number;
81
+ }): Promise<File>;
82
+ /**
83
+ * Hands back the file untouched when it already fits `MAX_IMAGE_BYTES`, and otherwise
84
+ * re-encodes it under the limit exactly as the editor would — same edge cap, same
85
+ * PNG-stays-PNG rule.
86
+ *
87
+ * This is for the paths that upload a picked file without opening the editor (the
88
+ * "upload the rest as-is" batch): they get the same guarantee a confirmed edit has,
89
+ * instead of handing media-service bytes it will refuse with a 413.
90
+ */
91
+ export declare function ensureUploadableImage(file: File, unreadableMessage?: string): Promise<File>;
46
92
  /** Converts Remy's base64 result into an uploadable file without re-encoding it. */
47
93
  export declare function dataUrlToFile(dataUrl: string, fileName?: string): Promise<File>;
48
94
  /** `confirm` needs the real pixel size, and only the browser has it before upload. */
@@ -72,6 +118,34 @@ export declare function uploadProductImage(itemId: string, file: File, options:
72
118
  imageId?: string;
73
119
  unreadableMessage?: string;
74
120
  }): Promise<FdoMenuV4ItemImage>;
121
+ /**
122
+ * Rewrites an existing image's original bytes, keeping its identity.
123
+ *
124
+ * This is what makes editing an uploaded photo cheap: `updateOriginal` overwrites the
125
+ * object in place, so `_id`, `url`, `isDefault`, `imageSet` and the item's dual-written
126
+ * `thumbnail` all stay as they are and the menu document needs no write at all. The
127
+ * returned image is the one that was passed in, unchanged — callers persist nothing.
128
+ *
129
+ * What does need doing is the ratios. media-service deliberately leaves them alone, so
130
+ * every ratio derived from the old photo is now stale and has to be queued again from the
131
+ * new one. Only the derived ones: a ratio whose `imageSet` value is set is a photo the
132
+ * merchant supplied for that shape, and re-deriving it would throw their work away. The
133
+ * requeue is `append` (via `generateAspectRatios`), never `create({ mode: "replace" })` —
134
+ * replace also deletes every variant missing from the request, which for a subset means
135
+ * deleting exactly the merchant's own ratios.
136
+ *
137
+ * `formatsRequeued` is reported rather than thrown: by the time a requeue can fail the
138
+ * original is already overwritten, and a rejected promise would tell the merchant their
139
+ * crop failed when it did not. They need to hear that the photo is saved and its formats
140
+ * are not, which only the caller can phrase.
141
+ *
142
+ * The caller must bump the image's cache stamp (`bumpImageVersion`) — the URL did not
143
+ * move, so nothing refetches on its own.
144
+ */
145
+ export declare function updateProductImageOriginal(itemId: string, image: FdoMenuV4ItemImage, file: File): Promise<{
146
+ image: FdoMenuV4ItemImage;
147
+ formatsRequeued: boolean;
148
+ }>;
75
149
  /**
76
150
  * A file the merchant picked before the item it belongs to exists.
77
151
  *
@@ -116,21 +190,30 @@ export declare function generateAspectRatios(itemId: string, image: FdoMenuV4Ite
116
190
  /** `/images/update` carries its bytes inline, so the file has to become a data URL. */
117
191
  export declare function readFileAsDataUrl(file: File): Promise<string>;
118
192
  /**
119
- * Substitutes the merchant's own photo for one generated ratio and marks it custom.
193
+ * Queues one ratio to be generated from a photo the merchant supplies, and marks it
194
+ * custom.
195
+ *
196
+ * Named "replace" after what it does for the merchant, not after what reaches storage:
197
+ * media-service stores these bytes as the generation *source* and hands them to the
198
+ * model (`/images/update` with `base64`), so the variant ends up a redraw of the
199
+ * supplied photo rather than the photo itself. Cropping to `ratio` in the editor is what
200
+ * frames the subject for the model. It no longer pads the source to the ratio first: the
201
+ * service trims uniform bars off a generation source, so the bars were discarded before
202
+ * the model ever saw them — a real crop at least lets the merchant choose what survives.
120
203
  *
121
- * The variant keeps its URL and only the bytes behind it are rewritten, so the only
122
- * difference between the input image and the returned one is `imageSet[ratio]`: it
123
- * now carries the variant URL as a marker meaning "not derived from the original".
124
- * That URL is the same `{url}?aspect_ratio={ratio}` the consumer would compose
125
- * anyway — the value exists to be checked for null, not to point somewhere else. The
126
- * caller persists the returned image to save the marker.
204
+ * So this is asynchronous and billable, exactly like `regenerateAspectRatio`: the ratio
205
+ * drops back to `pending` for ~40s and one AI generation is consumed. Callers must show
206
+ * the ratio as generating and poll `listImages` treating the returned image as final
207
+ * shows the merchant the old variant and calls it done.
127
208
  *
128
- * The caller also has to bust its own image cache the URL is "always latest" at the
129
- * edge, but a browser that already fetched it will not know.
209
+ * The variant keeps its URL, so the only difference between the input image and the
210
+ * returned one is `imageSet[ratio]`: it now carries the variant URL as a marker meaning
211
+ * "not derived from the original". That URL is the same `{url}?aspect_ratio={ratio}` the
212
+ * consumer would compose anyway — the value exists to be checked for null, not to point
213
+ * somewhere else. The caller persists the returned image to save the marker.
130
214
  *
131
- * The file is expected to be cropped to `ratio` and re-encoded by the dialog: these
132
- * bytes travel through the service pod (no variant-level upload-intent exists), and a
133
- * variant is supposed to be exactly its ratio.
215
+ * The caller also has to bust its own image cache once the generation lands — the URL is
216
+ * "always latest" at the edge, but a browser that already fetched it will not know.
134
217
  */
135
218
  export declare function replaceAspectRatioImage(itemId: string, image: FdoMenuV4ItemImage, ratio: AspectRatio, file: File): Promise<FdoMenuV4ItemImage>;
136
219
  /**
@@ -0,0 +1,56 @@
1
+ import "vue-advanced-cropper/dist/style.css";
2
+ /**
3
+ * What the dialog hands back: a thunk, not a file.
4
+ *
5
+ * Encoding a crop costs a full canvas re-encode, and the cropper emits `change` on every
6
+ * drag frame — emitting a File each time would re-encode a 12 MP photo dozens of times
7
+ * per gesture. So the parent gets a function and calls it once, on confirm.
8
+ */
9
+ export type ProduceEditedImage = () => Promise<File | null>;
10
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
11
+ mode: "upload" | "edit" | "replace";
12
+ /** Absent in `replace`, where the merchant picks the photo inside the dialog. */
13
+ file?: File | null | undefined;
14
+ /** Locks the crop shape. Only `replace` sets it. */
15
+ ratio?: string | null | undefined;
16
+ /** How many generated formats a save will re-derive; drives the `edit` notice. */
17
+ derivedFormats?: number | undefined;
18
+ }>, {
19
+ file: null;
20
+ ratio: null;
21
+ derivedFormats: number;
22
+ }>>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
23
+ mode: "upload" | "edit" | "replace";
24
+ /** Absent in `replace`, where the merchant picks the photo inside the dialog. */
25
+ file?: File | null | undefined;
26
+ /** Locks the crop shape. Only `replace` sets it. */
27
+ ratio?: string | null | undefined;
28
+ /** How many generated formats a save will re-derive; drives the `edit` notice. */
29
+ derivedFormats?: number | undefined;
30
+ }>, {
31
+ file: null;
32
+ ratio: null;
33
+ derivedFormats: number;
34
+ }>>> & Readonly<{}>, {
35
+ ratio: string | null;
36
+ file: File | null;
37
+ derivedFormats: number;
38
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
39
+ export default _default;
40
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
41
+ type __VLS_TypePropsToRuntimeProps<T> = {
42
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
43
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
44
+ } : {
45
+ type: import('vue').PropType<T[K]>;
46
+ required: true;
47
+ };
48
+ };
49
+ type __VLS_WithDefaults<P, D> = {
50
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
51
+ default: D[K];
52
+ }> : P[K];
53
+ };
54
+ type __VLS_Prettify<T> = {
55
+ [K in keyof T]: T[K];
56
+ } & {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The original behind `url` as a `File`, from memory when it has been read before.
3
+ *
4
+ * Returns null when it cannot be read, exactly like `fetchImageAsBlob` — a failure is
5
+ * never remembered, so the next click is free to try again.
6
+ */
7
+ export declare function readImageFile(url: string, imageId: string, filename?: string): Promise<File | null>;
@@ -0,0 +1,11 @@
1
+ /** Call after the bytes behind `imageId` have been rewritten. */
2
+ export declare function bumpImageVersion(imageId: string): void;
3
+ /**
4
+ * Appends the current stamp for `imageId`, if it has one.
5
+ *
6
+ * Untouched images are returned as-is rather than stamped with a constant: the URL is a
7
+ * cache key, and a first render that already differs from what POS requests would throw
8
+ * away a warm edge entry for nothing.
9
+ */
10
+ export declare function stampImageUrl(url: string, imageId: string): string;
11
+ export declare function stampImageUrl(url: string | null | undefined, imageId: string): string | null | undefined;
@@ -0,0 +1,10 @@
1
+ import type { FdoItemOverriderV4 } from "@feedmepos/entity";
2
+ /**
3
+ * Pick an item's base (non-variant) overrider out of an overrider list.
4
+ *
5
+ * Catalog/scheduler overrider maps only hold entries for items that were
6
+ * actually overridden, so the list is often absent entirely. When it exists it
7
+ * is ordered `[...variantCombinations, baseOverrider]`, so index 0 is not the
8
+ * base overrider once the item has variants.
9
+ */
10
+ export declare function pickBaseOverrider(overriders: FdoItemOverriderV4[] | undefined | null): FdoItemOverriderV4 | undefined;