Просмотр исходного кода

fix(inventory): hydrate Extra Colours; render Dual Color as bars; punch up Sparkle + checkerboard (#1154)

  @maugsburger surfaced four bugs against the original #1154 multi-colour
  swatch work:

  1. Editing an existing spool always opened with the Extra Colours field
     blank, even when the COLOR preview banner above it was rendering
     correctly from the saved data. ColorSection seeded its local
     ``extraColorsDraft`` via ``useState(formData.extra_colors)`` at
     mount time, but SpoolFormModal opens *before* its own useEffect
     populates ``formData`` from the spool record — so by the time the
     saved value landed, the input had already locked onto ''. The user
     then had to retype the value before saving anything else.

  2. Dual Color and Gradient produced the same diagonal blend
     (``linear-gradient(135deg, A, B)``), so the two variants were
     visually indistinguishable. The whole point of the Dual Color variant
     is that the spool has two distinct bars on the reel — a smooth blend
     defeats it.

  3. Sparkle was almost invisible on card-sized swatches. The original
     4-dot pattern (each ~1px) read fine on the inline 20×20 swatch but
     disappeared on the 60-pixel inventory card banners — exactly where
     the user actually identifies a spool.

  4. Checkerboard cell density scaled with the swatch — the same 4-cell
     pattern was either tiny squares on a small swatch or four huge
     squares on a card-sized banner. The user couldn't tell a translucent
     filament from a multi-colour one because the indicator changed shape.

  Fix:

  - ``ColorSection.tsx``: ref-guarded ``useEffect`` resyncs the draft
    whenever the parent's ``formData.extra_colors`` changes via an
    external update. ``commitExtraColors`` updates the ref before
    calling ``updateField`` so live user typing is round-tripped without
    the resync useEffect clobbering it.

  - ``filamentSwatchHelpers.ts: buildColorLayer``: branch on
    ``effect_type``. ``dual-color`` and ``tri-color`` produce
    ``linear-gradient(to right, c1 0 X%, c2 X% Y%, ...)`` with CSS
    double-position stops (hard line, not blend) and equal-width
    segments. ``gradient`` keeps the original 135° smooth blend. The
    ``multicolor`` conic-gradient path is untouched.

  - ``filamentSwatchHelpers.ts: EFFECT_OVERLAYS.sparkle``: bumped from 4
    dots to 13 flecks in mixed sizes (1 / 1.5 / 2 px) and varying
    opacity (0.65 → 1.0) for a depth-of-field "metal flake" feel.

  - ``filamentSwatchHelpers.ts: buildFilamentBackground``: now returns
    ``{ backgroundImage, backgroundSize }`` so per-layer sizes can be
    applied — painted layers stay ``cover``, the checkerboard gets a
    fixed 12px tile so cell density is constant regardless of element
    size. Updated the three existing call sites (``InventoryPage`` group
    banner + spool card, ``ColorSection`` preview) to spread the style
    object directly. ``FilamentSwatch.tsx`` composes the same per-layer
    sizing inline so its output stays in lockstep.

  Tests: 8 new frontend cases pinning the four fixes — Dual/Tri Color
  hard-split (3 tests + 1 regression guard that Dual ≠ Gradient for the
  same stops), Sparkle prominence (≥ 10 distinct radial-gradient layers
  in the rendered background), checkerboard density (last backgroundSize
  layer is a fixed pixel value, not ``cover``), 4 hydration cases (fills
  when formData arrives via parent update, resyncs when the spool
  changes mid-form, doesn't clobber live user typing, clears when the
  new spool has no extra_colors). Existing buildFilamentBackground tests
  updated for the new return-object shape. Full frontend suite: 1610
  passed; full backend suite: 3598 passed; no regressions.
maziggy 3 недель назад
Родитель
Сommit
86b54026f6

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
CHANGELOG.md


+ 42 - 11
frontend/src/__tests__/components/FilamentSwatch.test.tsx

@@ -133,7 +133,7 @@ describe('dual-color / tri-color hard-split bars (#1154 follow-up)', () => {
       extraColors: '7f3696,006ec9',
       extraColors: '7f3696,006ec9',
       effectType: 'dual-color',
       effectType: 'dual-color',
     });
     });
-    const lower = bg.toLowerCase();
+    const lower = bg.backgroundImage.toLowerCase();
     // Hard split direction — ``to right`` (or ``90deg``), never ``135deg``.
     // Hard split direction — ``to right`` (or ``90deg``), never ``135deg``.
     expect(lower).toContain('to right');
     expect(lower).toContain('to right');
     expect(lower).not.toContain('135deg');
     expect(lower).not.toContain('135deg');
@@ -151,7 +151,7 @@ describe('dual-color / tri-color hard-split bars (#1154 follow-up)', () => {
       extraColors: 'ff0000,00ff00,0000ff',
       extraColors: 'ff0000,00ff00,0000ff',
       effectType: 'tri-color',
       effectType: 'tri-color',
     });
     });
-    const lower = bg.toLowerCase();
+    const lower = bg.backgroundImage.toLowerCase();
     expect(lower).toContain('to right');
     expect(lower).toContain('to right');
     // Each third gets its own contiguous segment.
     // Each third gets its own contiguous segment.
     expect(lower).toMatch(/#ff0000\s+0\.000%\s+33\.333%/);
     expect(lower).toMatch(/#ff0000\s+0\.000%\s+33\.333%/);
@@ -164,7 +164,7 @@ describe('dual-color / tri-color hard-split bars (#1154 follow-up)', () => {
       extraColors: '7f3696,006ec9',
       extraColors: '7f3696,006ec9',
       effectType: 'gradient',
       effectType: 'gradient',
     });
     });
-    const lower = bg.toLowerCase();
+    const lower = bg.backgroundImage.toLowerCase();
     // Original visual preserved for non-dual / non-tri stops.
     // Original visual preserved for non-dual / non-tri stops.
     expect(lower).toContain('135deg');
     expect(lower).toContain('135deg');
     expect(lower).not.toContain('to right');
     expect(lower).not.toContain('to right');
@@ -187,27 +187,58 @@ describe('dual-color / tri-color hard-split bars (#1154 follow-up)', () => {
       extraColors: '7f3696,006ec9',
       extraColors: '7f3696,006ec9',
       effectType: 'gradient',
       effectType: 'gradient',
     });
     });
-    expect(dual).not.toBe(grad);
+    expect(dual.backgroundImage).not.toBe(grad.backgroundImage);
+  });
+});
+
+describe('Sparkle prominence + checkerboard density (#1154 follow-up cosmetic)', () => {
+  it('renders Sparkle with at least 10 distinct dots so it reads on card-sized swatches', () => {
+    // The original Sparkle pattern was 4 dots — too subtle on a 200×60px
+    // banner. The fix bumps it to 13 mixed-size flecks. Pin the contract
+    // at "at least 10" so future tweaks have headroom without the test
+    // breaking on every adjustment.
+    render(<FilamentSwatch rgba="ff0000ff" effectType="sparkle" />);
+    const el = screen.getByTestId('filament-swatch');
+    const radialCount = (el.style.backgroundImage.match(/radial-gradient/g) ?? []).length;
+    expect(radialCount).toBeGreaterThanOrEqual(10);
+  });
+
+  it('uses fixed-pixel checkerboard tile so cell density is independent of swatch size', () => {
+    // Without per-layer background-size, ``cover`` stretched the conic
+    // gradient over the whole element and a card-sized banner only showed
+    // 4 huge cells. Verify the checker layer carries an explicit pixel
+    // tile size.
+    const bg = buildFilamentBackground({ rgba: 'ff0000ff' });
+    const sizes = bg.backgroundSize.split(',').map((s) => s.trim());
+    // Last layer is the checker; should be a fixed pixel tile, not 'cover'.
+    expect(sizes[sizes.length - 1]).toMatch(/^\d+px(\s+\d+px)?$/);
+    expect(sizes[sizes.length - 1]).not.toContain('cover');
   });
   });
 });
 });
 
 
 describe('buildFilamentBackground', () => {
 describe('buildFilamentBackground', () => {
-  it('emits the same layered background string the component renders', () => {
+  it('emits a CSS-style object with layered images and per-layer sizes', () => {
     const bg = buildFilamentBackground({
     const bg = buildFilamentBackground({
       rgba: 'ff0000ff',
       rgba: 'ff0000ff',
       extraColors: 'aabbcc,ddeeff',
       extraColors: 'aabbcc,ddeeff',
       effectType: 'matte',
       effectType: 'matte',
     });
     });
     // Effect overlay → colour layer → checkerboard, in that order.
     // Effect overlay → colour layer → checkerboard, in that order.
-    expect(bg).toMatch(/linear-gradient/);
-    expect(bg).toMatch(/repeating-conic-gradient/);
-    expect(bg.toLowerCase()).toContain('#aabbcc');
-    expect(bg.toLowerCase()).toContain('#ddeeff');
+    expect(bg.backgroundImage).toMatch(/linear-gradient/);
+    expect(bg.backgroundImage).toMatch(/repeating-conic-gradient/);
+    expect(bg.backgroundImage.toLowerCase()).toContain('#aabbcc');
+    expect(bg.backgroundImage.toLowerCase()).toContain('#ddeeff');
+    // Per-layer sizes — three comma-separated values (effect/colour/checker)
+    // in the same order. The checker has a fixed pixel tile so the cell
+    // density doesn't scale with the element (#1154 follow-up).
+    const sizeParts = bg.backgroundSize.split(',').map((s) => s.trim());
+    expect(sizeParts).toHaveLength(3);
+    expect(sizeParts[2]).toMatch(/\d+px/);
   });
   });
 
 
   it('returns a usable solid background when only rgba is provided', () => {
   it('returns a usable solid background when only rgba is provided', () => {
     const bg = buildFilamentBackground({ rgba: '00ff00ff' });
     const bg = buildFilamentBackground({ rgba: '00ff00ff' });
-    expect(bg.toLowerCase()).toContain('#00ff00ff');
-    expect(bg).toMatch(/repeating-conic-gradient/);
+    expect(bg.backgroundImage.toLowerCase()).toContain('#00ff00ff');
+    expect(bg.backgroundImage).toMatch(/repeating-conic-gradient/);
   });
   });
 });
 });

+ 12 - 6
frontend/src/components/FilamentSwatch.tsx

@@ -1,6 +1,7 @@
 import React, { useMemo } from 'react';
 import React, { useMemo } from 'react';
 import {
 import {
   CHECKERBOARD_BG,
   CHECKERBOARD_BG,
+  CHECKERBOARD_TILE_SIZE,
   EFFECT_OVERLAYS,
   EFFECT_OVERLAYS,
   buildColorLayer,
   buildColorLayer,
   parseStops,
   parseStops,
@@ -52,11 +53,16 @@ export function FilamentSwatch({
   const effectLayer = effectKey ? EFFECT_OVERLAYS[effectKey] ?? null : null;
   const effectLayer = effectKey ? EFFECT_OVERLAYS[effectKey] ?? null : null;
 
 
   // Layer order (top → bottom): effect overlay → colour layer → checkerboard.
   // Layer order (top → bottom): effect overlay → colour layer → checkerboard.
-  // Set as `background-image` (not the `background` shorthand) so the value
-  // remains a pure list-of-images that browsers and test runners parse cleanly.
-  const backgroundImage = [effectLayer, colorLayer, CHECKERBOARD_BG]
-    .filter((layer): layer is string => Boolean(layer))
-    .join(', ');
+  // Per-layer background-size: 'cover' on the painted layers, fixed tile on
+  // the checkerboard so its cell density doesn't scale with element size
+  // (a card-sized swatch with `cover` checker would render only 4 huge
+  // cells; #1154 follow-up).
+  const layers: { image: string; size: string }[] = [];
+  if (effectLayer) layers.push({ image: effectLayer, size: 'cover' });
+  layers.push({ image: colorLayer, size: 'cover' });
+  layers.push({ image: CHECKERBOARD_BG, size: CHECKERBOARD_TILE_SIZE });
+  const backgroundImage = layers.map((l) => l.image).join(', ');
+  const backgroundSize = layers.map((l) => l.size).join(', ');
 
 
   const shapeClass =
   const shapeClass =
     shape === 'circle' ? 'rounded-full' : shape === 'pill' ? 'rounded-full' : 'rounded';
     shape === 'circle' ? 'rounded-full' : shape === 'pill' ? 'rounded-full' : 'rounded';
@@ -74,7 +80,7 @@ export function FilamentSwatch({
     <span
     <span
       data-testid="filament-swatch"
       data-testid="filament-swatch"
       className={`${className} ${shapeClass} border border-black/20 inline-block flex-shrink-0`}
       className={`${className} ${shapeClass} border border-black/20 inline-block flex-shrink-0`}
-      style={{ backgroundImage, backgroundSize: 'cover', ...style }}
+      style={{ backgroundImage, backgroundSize, ...style }}
       title={computedTitle}
       title={computedTitle}
     />
     />
   );
   );

+ 46 - 11
frontend/src/components/filamentSwatchHelpers.ts

@@ -68,8 +68,16 @@ export const FILAMENT_EFFECT_OPTIONS: ReadonlyArray<{
 // Checkerboard pattern shown beneath the colour layer so alpha < FF is
 // Checkerboard pattern shown beneath the colour layer so alpha < FF is
 // actually visible to the user. Kept as a pure gradient (no position/size)
 // actually visible to the user. Kept as a pure gradient (no position/size)
 // so the value parses cleanly inside `background-image:` everywhere.
 // so the value parses cleanly inside `background-image:` everywhere.
+//
+// Density is controlled by ``CHECKERBOARD_TILE_SIZE`` applied as
+// ``background-size`` on this layer specifically — without that,
+// ``backgroundSize: 'cover'`` would stretch the gradient to the whole
+// element and a card-sized swatch would only show 4 huge cells (#1154
+// follow-up reporter feedback). Per-layer sizing is supported by every
+// modern browser via comma-separated ``background-size``.
 export const CHECKERBOARD_BG =
 export const CHECKERBOARD_BG =
   'repeating-conic-gradient(#cbcbcb 0% 25%, #f5f5f5 0% 50%)';
   'repeating-conic-gradient(#cbcbcb 0% 25%, #f5f5f5 0% 50%)';
+export const CHECKERBOARD_TILE_SIZE = '12px 12px';
 
 
 /** Optional CSS overlay layer for variants that have a visual treatment.
 /** Optional CSS overlay layer for variants that have a visual treatment.
  *  Variants without an entry are categorical labels only — they don't paint
  *  Variants without an entry are categorical labels only — they don't paint
@@ -77,12 +85,24 @@ export const CHECKERBOARD_BG =
  *  effect is to switch the colour layer to a conic-gradient (see
  *  effect is to switch the colour layer to a conic-gradient (see
  *  `buildColorLayer`), not to add an overlay layer. */
  *  `buildColorLayer`), not to add an overlay layer. */
 export const EFFECT_OVERLAYS: Partial<Record<FilamentEffect, string>> = {
 export const EFFECT_OVERLAYS: Partial<Record<FilamentEffect, string>> = {
-  // Sparkle: fine bright dots scattered across the swatch.
+  // Sparkle: bright flecks scattered across the swatch. The original 4-dot
+  // pattern was too subtle on card-sized swatches (#1154 follow-up); 13
+  // dots in mixed sizes (1px / 1.5px / 2px) and opacities give depth and
+  // make the variant clearly distinguishable from solid + multicolor.
   sparkle:
   sparkle:
-    'radial-gradient(circle at 30% 20%, rgba(255,255,255,0.85) 0 1px, transparent 1.5px), ' +
-    'radial-gradient(circle at 70% 60%, rgba(255,255,255,0.7) 0 1px, transparent 1.5px), ' +
-    'radial-gradient(circle at 45% 75%, rgba(255,255,255,0.6) 0 1px, transparent 1.5px), ' +
-    'radial-gradient(circle at 80% 30%, rgba(255,255,255,0.5) 0 1px, transparent 1.5px)',
+    'radial-gradient(circle at 12% 18%, rgba(255,255,255,0.95) 0 1.5px, transparent 2px), ' +
+    'radial-gradient(circle at 28% 42%, rgba(255,255,255,0.85) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 38% 78%, rgba(255,255,255,0.95) 0 1.5px, transparent 2px), ' +
+    'radial-gradient(circle at 52% 12%, rgba(255,255,255,0.80) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 58% 55%, rgba(255,255,255,1) 0 2px, transparent 2.5px), ' +
+    'radial-gradient(circle at 68% 28%, rgba(255,255,255,0.75) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 75% 88%, rgba(255,255,255,0.85) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 82% 48%, rgba(255,255,255,0.95) 0 1.5px, transparent 2px), ' +
+    'radial-gradient(circle at 88% 18%, rgba(255,255,255,0.80) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 92% 70%, rgba(255,255,255,0.85) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 18% 62%, rgba(255,255,255,0.75) 0 1px, transparent 1.5px), ' +
+    'radial-gradient(circle at 45% 32%, rgba(255,255,255,0.65) 0 0.8px, transparent 1.2px), ' +
+    'radial-gradient(circle at 65% 72%, rgba(255,255,255,0.65) 0 0.8px, transparent 1.2px)',
   // Wood: subtle horizontal banding to mimic grain.
   // Wood: subtle horizontal banding to mimic grain.
   wood:
   wood:
     'repeating-linear-gradient(90deg, ' +
     'repeating-linear-gradient(90deg, ' +
@@ -176,14 +196,18 @@ export function buildColorLayer(
 
 
 /** Public helper: produce a CSS background-image value (list of layered
 /** Public helper: produce a CSS background-image value (list of layered
  *  <image>s) for a filament, for callers that want to paint a banner or
  *  <image>s) for a filament, for callers that want to paint a banner or
- *  large area instead of using the swatch element. Pair with
- *  `background-size: cover` and the swatch logic stays consistent. */
+ *  large area instead of using the swatch element. Returns a
+ *  ``CSSProperties``-compatible object with ``backgroundImage`` and
+ *  ``backgroundSize`` so the checkerboard underlayer keeps a fixed tile
+ *  density regardless of element size — without per-layer sizing, a
+ *  card-sized banner only shows 4 huge checker cells.
+ */
 export function buildFilamentBackground(opts: {
 export function buildFilamentBackground(opts: {
   rgba?: string | null;
   rgba?: string | null;
   extraColors?: string | null;
   extraColors?: string | null;
   effectType?: FilamentEffect | string | null;
   effectType?: FilamentEffect | string | null;
   subtype?: string | null;
   subtype?: string | null;
-}): string {
+}): { backgroundImage: string; backgroundSize: string } {
   const stops = parseStops(opts.extraColors);
   const stops = parseStops(opts.extraColors);
   const colorLayer = buildColorLayer(opts.rgba, stops, opts.subtype, opts.effectType);
   const colorLayer = buildColorLayer(opts.rgba, stops, opts.subtype, opts.effectType);
   const effectKey =
   const effectKey =
@@ -191,7 +215,18 @@ export function buildFilamentBackground(opts: {
       ? (opts.effectType as FilamentEffect)
       ? (opts.effectType as FilamentEffect)
       : null;
       : null;
   const effectLayer = effectKey ? EFFECT_OVERLAYS[effectKey] ?? null : null;
   const effectLayer = effectKey ? EFFECT_OVERLAYS[effectKey] ?? null : null;
-  return [effectLayer, colorLayer, CHECKERBOARD_BG]
-    .filter((layer): layer is string => Boolean(layer))
-    .join(', ');
+
+  // Layer order (top → bottom): effect overlay → colour layer → checkerboard.
+  // Per-layer background-size: 'cover' on the painted layers (so they fill
+  // the element) and the fixed tile size on the checkerboard so the cell
+  // count scales with the element rather than the element scaling the cells.
+  const layers: { image: string; size: string }[] = [];
+  if (effectLayer) layers.push({ image: effectLayer, size: 'cover' });
+  layers.push({ image: colorLayer, size: 'cover' });
+  layers.push({ image: CHECKERBOARD_BG, size: CHECKERBOARD_TILE_SIZE });
+
+  return {
+    backgroundImage: layers.map((l) => l.image).join(', '),
+    backgroundSize: layers.map((l) => l.size).join(', '),
+  };
 }
 }

+ 1 - 1
frontend/src/components/spool-form/ColorSection.tsx

@@ -216,7 +216,7 @@ export function ColorSection({
       {/* Color preview banner — shows gradient + effect overlay. */}
       {/* Color preview banner — shows gradient + effect overlay. */}
       <div
       <div
         className="h-10 rounded-lg border border-bambu-dark-tertiary"
         className="h-10 rounded-lg border border-bambu-dark-tertiary"
-        style={{ backgroundImage: previewBackground, backgroundSize: 'cover' }}
+        style={previewBackground}
         data-testid="color-preview-banner"
         data-testid="color-preview-banner"
       />
       />
 
 

+ 4 - 4
frontend/src/pages/InventoryPage.tsx

@@ -1295,7 +1295,7 @@ function InventoryPage() {
               {pagedItems.map((item) => {
               {pagedItems.map((item) => {
                 if (item.type === 'group') {
                 if (item.type === 'group') {
                   const { key, spools: groupSpools, representative: rep } = item;
                   const { key, spools: groupSpools, representative: rep } = item;
-                  const groupBannerImage = buildFilamentBackground({
+                  const groupBannerStyle = buildFilamentBackground({
                     rgba: rep.rgba,
                     rgba: rep.rgba,
                     extraColors: rep.extra_colors,
                     extraColors: rep.extra_colors,
                     effectType: rep.effect_type,
                     effectType: rep.effect_type,
@@ -1309,7 +1309,7 @@ function InventoryPage() {
                         className="bg-bambu-dark-secondary rounded-lg overflow-hidden border border-bambu-green/30 hover:border-bambu-green transition-colors cursor-pointer"
                         className="bg-bambu-dark-secondary rounded-lg overflow-hidden border border-bambu-green/30 hover:border-bambu-green transition-colors cursor-pointer"
                         onClick={() => toggleGroupExpand(key)}
                         onClick={() => toggleGroupExpand(key)}
                       >
                       >
-                        <div className="h-10 flex items-center px-4 gap-3" style={{ backgroundImage: groupBannerImage, backgroundSize: 'cover' }}>
+                        <div className="h-10 flex items-center px-4 gap-3" style={groupBannerStyle}>
                           <span className="bg-white/90 text-gray-800 px-3 py-0.5 rounded-full text-sm font-medium">
                           <span className="bg-white/90 text-gray-800 px-3 py-0.5 rounded-full text-sm font-medium">
                             {resolveSpoolColorName(rep.color_name, rep.rgba) || '-'}
                             {resolveSpoolColorName(rep.color_name, rep.rgba) || '-'}
                           </span>
                           </span>
@@ -1677,7 +1677,7 @@ function SpoolCard({
   onClick: () => void;
   onClick: () => void;
   t: (key: string, opts?: Record<string, unknown>) => string;
   t: (key: string, opts?: Record<string, unknown>) => string;
 }) {
 }) {
-  const bannerImage = buildFilamentBackground({
+  const bannerStyle = buildFilamentBackground({
     rgba: spool.rgba,
     rgba: spool.rgba,
     extraColors: spool.extra_colors,
     extraColors: spool.extra_colors,
     effectType: spool.effect_type,
     effectType: spool.effect_type,
@@ -1688,7 +1688,7 @@ function SpoolCard({
       className={`bg-bambu-dark-secondary rounded-lg overflow-hidden border border-bambu-dark-tertiary hover:border-bambu-green transition-colors cursor-pointer ${spool.archived_at ? 'opacity-50' : ''}`}
       className={`bg-bambu-dark-secondary rounded-lg overflow-hidden border border-bambu-dark-tertiary hover:border-bambu-green transition-colors cursor-pointer ${spool.archived_at ? 'opacity-50' : ''}`}
       onClick={onClick}
       onClick={onClick}
     >
     >
-      <div className="h-14 flex items-center justify-center" style={{ backgroundImage: bannerImage, backgroundSize: 'cover' }}>
+      <div className="h-14 flex items-center justify-center" style={bannerStyle}>
         <span className="bg-white/90 text-gray-800 px-3 py-0.5 rounded-full text-sm font-medium">
         <span className="bg-white/90 text-gray-800 px-3 py-0.5 rounded-full text-sm font-medium">
           {resolveSpoolColorName(spool.color_name, spool.rgba) || '-'}
           {resolveSpoolColorName(spool.color_name, spool.rgba) || '-'}
         </span>
         </span>

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-Ct29kBC5.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
 
     <!-- Splash screens for iOS -->
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-DUxwTpts.js"></script>
+    <script type="module" crossorigin src="/assets/index-Ct29kBC5.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-7GmlJb0k.css">
     <link rel="stylesheet" crossorigin href="/assets/index-7GmlJb0k.css">
   </head>
   </head>
   <body>
   <body>

Некоторые файлы не были показаны из-за большого количества измененных файлов