@bleedingdev/modern-js-create 3.2.0-ultramodern.29 → 3.2.0-ultramodern.30

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/dist/index.js CHANGED
@@ -453,8 +453,8 @@ const EN_LOCALE = {
453
453
  success: '✨ Created successfully!',
454
454
  nextSteps: '📋 Next steps:',
455
455
  step1: 'cd {projectName}',
456
- step2: 'pnpm install',
457
- step3: 'pnpm dev'
456
+ step2: 'mise exec -- pnpm install',
457
+ step3: 'mise exec -- pnpm dev'
458
458
  },
459
459
  help: {
460
460
  title: '🚀 Modern.js Project Creator',
@@ -510,8 +510,8 @@ const ZH_LOCALE = {
510
510
  success: '✨ 创建成功!',
511
511
  nextSteps: '📋 下一步:',
512
512
  step1: 'cd {projectName}',
513
- step2: 'pnpm install',
514
- step3: 'pnpm dev'
513
+ step2: 'mise exec -- pnpm install',
514
+ step3: 'mise exec -- pnpm dev'
515
515
  },
516
516
  help: {
517
517
  title: '🚀 Modern.js 项目创建工具',
@@ -567,13 +567,14 @@ const TAILWIND_VERSION = '4.3.0';
567
567
  const TAILWIND_POSTCSS_VERSION = '4.3.0';
568
568
  const EFFECT_TSGO_VERSION = '0.11.0';
569
569
  const TYPESCRIPT_VERSION = '6.0.3';
570
- const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260526.1';
570
+ const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260527.2';
571
571
  const OXLINT_VERSION = '1.66.0';
572
572
  const OXFMT_VERSION = '0.51.0';
573
573
  const ULTRACITE_VERSION = '7.7.0';
574
574
  const I18NEXT_VERSION = '26.2.0';
575
575
  const REACT_VERSION = '^19.2.6';
576
576
  const REACT_DOM_VERSION = '^19.2.6';
577
+ const PNPM_VERSION = '11.4.0';
577
578
  const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
578
579
  const GENERATED_CONTRACT_PATH = '.modernjs/ultramodern-generated-contract.json';
579
580
  const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
@@ -1097,7 +1098,7 @@ function createRootPackageJson(scope, packageSource) {
1097
1098
  name: scope,
1098
1099
  version: '0.1.0',
1099
1100
  type: 'module',
1100
- packageManager: 'pnpm@11.4.0',
1101
+ packageManager: `pnpm@${PNPM_VERSION}`,
1101
1102
  scripts: {
1102
1103
  dev: `pnpm --parallel --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} --filter ${ultramodern_workspace_packageName(scope, 'remote-commerce')} --filter ${ultramodern_workspace_packageName(scope, 'remote-identity')} --filter ${ultramodern_workspace_packageName(scope, 'remote-design-system')} dev`,
1103
1104
  'dev:shell': `pnpm --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} dev`,
@@ -1122,7 +1123,7 @@ function createRootPackageJson(scope, packageSource) {
1122
1123
  },
1123
1124
  engines: {
1124
1125
  node: '>=20',
1125
- pnpm: '>=11.4.0 <11.5.0'
1126
+ pnpm: `>=${PNPM_VERSION} <11.5.0`
1126
1127
  },
1127
1128
  workspaces: [
1128
1129
  'apps/*',
@@ -1674,8 +1675,9 @@ export default defineRuntimeConfig({
1674
1675
  function createAppStyles(enableTailwind) {
1675
1676
  return `${enableTailwind ? "@import 'tailwindcss';\n\n" : ''}:root {
1676
1677
  color: #10231c;
1677
- background: #f6f8f7;
1678
+ background: #f1eadc;
1678
1679
  font-family:
1680
+ Geist,
1679
1681
  Inter,
1680
1682
  ui-sans-serif,
1681
1683
  system-ui,
@@ -1703,6 +1705,288 @@ nav {
1703
1705
  a {
1704
1706
  color: #166b4b;
1705
1707
  }
1708
+
1709
+ .commerce-shell {
1710
+ background: #f1eadc;
1711
+ color: #0b0a08;
1712
+ min-height: 100vh;
1713
+ padding: 1.5rem clamp(1rem, 4vw, 3rem) 4rem;
1714
+ }
1715
+
1716
+ .commerce-header,
1717
+ .commerce-footer {
1718
+ align-items: center;
1719
+ background: rgba(255, 255, 255, 0.86);
1720
+ box-shadow: 0 0.625rem 1.875rem rgba(25, 20, 12, 0.08);
1721
+ display: flex;
1722
+ gap: 1.25rem;
1723
+ justify-content: space-between;
1724
+ margin: 0 auto;
1725
+ max-width: 88rem;
1726
+ padding: 1.25rem 1.75rem;
1727
+ }
1728
+
1729
+ .commerce-logo {
1730
+ font-size: 1.35rem;
1731
+ font-weight: 800;
1732
+ }
1733
+
1734
+ .commerce-nav,
1735
+ .commerce-actions,
1736
+ .commerce-language {
1737
+ align-items: center;
1738
+ display: flex;
1739
+ flex-wrap: wrap;
1740
+ gap: 0.75rem;
1741
+ }
1742
+
1743
+ .commerce-nav {
1744
+ margin: 0;
1745
+ }
1746
+
1747
+ .commerce-pill,
1748
+ .commerce-button,
1749
+ .commerce-link-button,
1750
+ .commerce-cart-button,
1751
+ .commerce-quantity-button {
1752
+ align-items: center;
1753
+ border-radius: 999px;
1754
+ border: 0.0625rem solid rgba(23, 23, 23, 0.14);
1755
+ box-shadow: 0 0.25rem 0.75rem rgba(20, 17, 10, 0.08);
1756
+ color: #14120d;
1757
+ display: inline-flex;
1758
+ font: inherit;
1759
+ font-weight: 750;
1760
+ justify-content: center;
1761
+ min-height: 2.5rem;
1762
+ padding: 0.65rem 1.05rem;
1763
+ text-decoration: none;
1764
+ }
1765
+
1766
+ .commerce-button {
1767
+ background: #00624b;
1768
+ border-color: #00624b;
1769
+ color: #ffffff;
1770
+ }
1771
+
1772
+ .commerce-link-button,
1773
+ .commerce-pill,
1774
+ .commerce-cart-button,
1775
+ .commerce-quantity-button {
1776
+ background: rgba(255, 255, 255, 0.92);
1777
+ }
1778
+
1779
+ .commerce-page {
1780
+ margin: 3rem auto 0;
1781
+ max-width: 88rem;
1782
+ }
1783
+
1784
+ .commerce-product {
1785
+ align-items: center;
1786
+ display: grid;
1787
+ gap: clamp(2rem, 5vw, 4rem);
1788
+ grid-template-columns: minmax(0, 1fr) minmax(20rem, 0.95fr);
1789
+ }
1790
+
1791
+ .commerce-product-media {
1792
+ aspect-ratio: 1 / 0.92;
1793
+ background:
1794
+ linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0) 42%),
1795
+ linear-gradient(135deg, #97c66d 0 20%, #6f9748 20% 34%, #d6c15d 34% 36%, #6a8f3e 36% 46%, #315824 46% 64%, #8bb85e 64% 100%);
1796
+ border: 1.25rem solid #ffe987;
1797
+ border-radius: 1.6rem;
1798
+ box-shadow: inset 0 -7rem 8rem rgba(58, 77, 35, 0.22);
1799
+ overflow: hidden;
1800
+ }
1801
+
1802
+ .commerce-product-media::after {
1803
+ background:
1804
+ radial-gradient(circle at 27% 76%, #1e2422 0 5%, transparent 5.4%),
1805
+ radial-gradient(circle at 55% 76%, #1e2422 0 6%, transparent 6.4%),
1806
+ linear-gradient(0deg, #004b7b 0 100%);
1807
+ border-radius: 1.2rem;
1808
+ content: "";
1809
+ display: block;
1810
+ height: 19%;
1811
+ margin: 58% auto 0;
1812
+ width: 42%;
1813
+ }
1814
+
1815
+ .commerce-eyebrow {
1816
+ color: #00624b;
1817
+ font-size: 0.85rem;
1818
+ font-weight: 850;
1819
+ letter-spacing: 0.16rem;
1820
+ text-transform: uppercase;
1821
+ }
1822
+
1823
+ .commerce-title {
1824
+ font-size: clamp(2.5rem, 6vw, 4.8rem);
1825
+ line-height: 0.95;
1826
+ margin: 0.65rem 0 1.4rem;
1827
+ }
1828
+
1829
+ .commerce-lede {
1830
+ color: #555149;
1831
+ font-size: 1.2rem;
1832
+ line-height: 1.65;
1833
+ max-width: 42rem;
1834
+ }
1835
+
1836
+ .commerce-facts {
1837
+ display: grid;
1838
+ gap: 1rem;
1839
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1840
+ margin: 2rem 0;
1841
+ }
1842
+
1843
+ .commerce-fact,
1844
+ .commerce-card,
1845
+ .commerce-cart-panel {
1846
+ background: rgba(255, 255, 255, 0.92);
1847
+ border-radius: 1rem;
1848
+ box-shadow: 0 0.5rem 1.25rem rgba(25, 20, 12, 0.08);
1849
+ padding: 1.25rem;
1850
+ }
1851
+
1852
+ .commerce-fact span,
1853
+ .commerce-card span {
1854
+ color: #767067;
1855
+ display: block;
1856
+ font-weight: 750;
1857
+ margin-bottom: 0.45rem;
1858
+ }
1859
+
1860
+ .commerce-fact strong {
1861
+ font-size: 1.1rem;
1862
+ }
1863
+
1864
+ .commerce-checkout {
1865
+ align-items: center;
1866
+ display: flex;
1867
+ flex-wrap: wrap;
1868
+ gap: 0.75rem;
1869
+ }
1870
+
1871
+ .commerce-section-title {
1872
+ font-size: 1.8rem;
1873
+ margin: 4.5rem 0 1.5rem;
1874
+ }
1875
+
1876
+ .commerce-grid {
1877
+ display: grid;
1878
+ gap: 1rem;
1879
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1880
+ }
1881
+
1882
+ .commerce-card strong {
1883
+ display: block;
1884
+ font-size: 1.45rem;
1885
+ }
1886
+
1887
+ .commerce-cart-panel {
1888
+ margin-top: 2rem;
1889
+ }
1890
+
1891
+ .commerce-cart-line {
1892
+ align-items: center;
1893
+ border-top: 0.0625rem solid rgba(23, 23, 23, 0.12);
1894
+ display: grid;
1895
+ gap: 1rem;
1896
+ grid-template-columns: minmax(0, 1fr) auto;
1897
+ padding: 1rem 0;
1898
+ }
1899
+
1900
+ .commerce-cart-line:first-of-type {
1901
+ border-top: 0;
1902
+ }
1903
+
1904
+ .commerce-quantity {
1905
+ align-items: center;
1906
+ display: flex;
1907
+ gap: 0.45rem;
1908
+ }
1909
+
1910
+ .commerce-quantity-button {
1911
+ min-height: 2rem;
1912
+ min-width: 2rem;
1913
+ padding: 0.25rem;
1914
+ }
1915
+
1916
+ .commerce-boundary-toggle {
1917
+ align-items: center;
1918
+ background: rgba(255, 255, 255, 0.92);
1919
+ border-radius: 0.8rem;
1920
+ bottom: 1.5rem;
1921
+ box-shadow: 0 0.75rem 2rem rgba(18, 15, 10, 0.14);
1922
+ display: flex;
1923
+ gap: 0.65rem;
1924
+ left: 1.5rem;
1925
+ padding: 0.8rem 1rem;
1926
+ position: fixed;
1927
+ z-index: 80;
1928
+ }
1929
+
1930
+ .commerce-boundary-toggle input {
1931
+ accent-color: #00624b;
1932
+ height: 1rem;
1933
+ width: 1rem;
1934
+ }
1935
+
1936
+ .boundary-overlay {
1937
+ inset: 0;
1938
+ pointer-events: none;
1939
+ position: fixed;
1940
+ z-index: 70;
1941
+ }
1942
+
1943
+ .boundary-overlay__box {
1944
+ border: 0.0625rem solid var(--boundary-color);
1945
+ border-radius: 0.55rem;
1946
+ box-shadow:
1947
+ 0 0 0 0.0625rem rgba(255, 255, 255, 0.72),
1948
+ 0 0.35rem 1.25rem color-mix(in srgb, var(--boundary-color) 20%, transparent);
1949
+ position: fixed;
1950
+ }
1951
+
1952
+ .boundary-overlay__label {
1953
+ background: color-mix(in srgb, var(--boundary-color) 88%, white);
1954
+ border-radius: 999px;
1955
+ color: #0b0a08;
1956
+ font-size: 0.7rem;
1957
+ font-weight: 850;
1958
+ line-height: 1;
1959
+ padding: 0.3rem 0.55rem;
1960
+ position: absolute;
1961
+ right: 0.35rem;
1962
+ top: 0.35rem;
1963
+ white-space: nowrap;
1964
+ }
1965
+
1966
+ .boundary-overlay__box[data-label-placement="above"] .boundary-overlay__label {
1967
+ bottom: calc(100% + 0.25rem);
1968
+ top: auto;
1969
+ }
1970
+
1971
+ @media (max-width: 860px) {
1972
+ .commerce-header,
1973
+ .commerce-footer,
1974
+ .commerce-product,
1975
+ .commerce-grid,
1976
+ .commerce-facts {
1977
+ grid-template-columns: 1fr;
1978
+ }
1979
+
1980
+ .commerce-header,
1981
+ .commerce-footer {
1982
+ align-items: flex-start;
1983
+ flex-direction: column;
1984
+ }
1985
+
1986
+ .commerce-product-media {
1987
+ min-height: 20rem;
1988
+ }
1989
+ }
1706
1990
  `;
1707
1991
  }
1708
1992
  function createPostcssConfig() {
@@ -1759,7 +2043,6 @@ const LocalizedHead = () => {
1759
2043
  function createShellPage() {
1760
2044
  return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
1761
2045
  import { ultramodernUiMarker } from '../../ultramodern-build';
1762
- import '../index.css';
1763
2046
 
1764
2047
  const languageCodes = ['en', 'cs'] as const;
1765
2048
 
@@ -1800,6 +2083,7 @@ export default function ShellHome() {
1800
2083
  `;
1801
2084
  }
1802
2085
  function createRemotePage(app) {
2086
+ if ('remote-commerce' === app.id) return createCommerceRemotePage(app);
1803
2087
  const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
1804
2088
  import { useEffect, useState } from 'react';
1805
2089
  import { ultramodernUiMarker } from '../../ultramodern-build';
@@ -1830,8 +2114,7 @@ import { ultramodernUiMarker } from '../../ultramodern-build';
1830
2114
  ` : '';
1831
2115
  const effectBffMarkup = appHasEffectApi(app) ? ` <p data-testid="effect-bff-status">{effectApiStatus}</p>
1832
2116
  ` : '';
1833
- return `${effectBffImport}import '../index.css';
1834
-
2117
+ return `${effectBffImport}
1835
2118
  ${createLocalizedHeadComponent()}
1836
2119
  export default function ${toPascalCase(app.id)}Home() {
1837
2120
  const { i18nInstance, language } = useModernI18n();
@@ -1860,6 +2143,399 @@ ${effectBffMarkup} </main>
1860
2143
  }
1861
2144
  `;
1862
2145
  }
2146
+ function createCommerceRemotePage(app) {
2147
+ return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2148
+ import { useEffect, useState, type CSSProperties } from 'react';
2149
+ import { ultramodernUiMarker } from '../../ultramodern-build';
2150
+
2151
+ const languageCodes = ['en', 'cs'] as const;
2152
+
2153
+ const boundaryDefinitions = [
2154
+ {
2155
+ color: '#ff5a57',
2156
+ id: 'explore',
2157
+ labelKey: 'commerce.boundaries.explore',
2158
+ },
2159
+ {
2160
+ color: '#24d671',
2161
+ id: 'decide',
2162
+ labelKey: 'commerce.boundaries.decide',
2163
+ },
2164
+ {
2165
+ color: '#f4d044',
2166
+ id: 'checkout',
2167
+ labelKey: 'commerce.boundaries.checkout',
2168
+ },
2169
+ ] as const;
2170
+
2171
+ type BoundaryId = (typeof boundaryDefinitions)[number]['id'];
2172
+ type BoundaryDefinition = (typeof boundaryDefinitions)[number];
2173
+
2174
+ const boundaryMetadata: Record<BoundaryId, BoundaryDefinition> = {
2175
+ checkout: boundaryDefinitions[2],
2176
+ decide: boundaryDefinitions[1],
2177
+ explore: boundaryDefinitions[0],
2178
+ };
2179
+
2180
+ const products = [
2181
+ {
2182
+ id: 'field-loader-112',
2183
+ titleKey: 'commerce.products.fieldLoader.title',
2184
+ descriptionKey: 'commerce.products.fieldLoader.description',
2185
+ priceKey: 'commerce.products.fieldLoader.price',
2186
+ powerKey: 'commerce.products.fieldLoader.power',
2187
+ availabilityKey: 'commerce.products.fieldLoader.availability',
2188
+ },
2189
+ {
2190
+ id: 'orchard-tractor',
2191
+ titleKey: 'commerce.products.orchard.title',
2192
+ badgeKey: 'commerce.products.orchard.badge',
2193
+ },
2194
+ {
2195
+ id: 'autonomy-kit',
2196
+ titleKey: 'commerce.products.autonomy.title',
2197
+ badgeKey: 'commerce.products.autonomy.badge',
2198
+ },
2199
+ ] as const;
2200
+
2201
+ type ProductId = (typeof products)[number]['id'];
2202
+ type CartState = Partial<Record<ProductId, number>>;
2203
+
2204
+ type BoundaryLabels = Record<BoundaryId, string>;
2205
+ type BoundaryBox = {
2206
+ color: string;
2207
+ height: number;
2208
+ id: BoundaryId;
2209
+ label: string;
2210
+ labelPlacement: 'above' | 'inside';
2211
+ left: number;
2212
+ top: number;
2213
+ width: number;
2214
+ };
2215
+
2216
+ const featuredProduct = products[0];
2217
+ const recommendations = [products[1], products[2]] as const;
2218
+
2219
+ ${createLocalizedHeadComponent()}
2220
+ const isBoundaryId = (value: string): value is BoundaryId =>
2221
+ Object.prototype.hasOwnProperty.call(boundaryMetadata, value);
2222
+
2223
+ function collectBoundaryBoxes(labels: BoundaryLabels): BoundaryBox[] {
2224
+ return Array.from(
2225
+ document.querySelectorAll<HTMLElement>('[data-boundary], [data-boundary-page]'),
2226
+ )
2227
+ .map(element => {
2228
+ const id = element.dataset.boundary ?? element.dataset.boundaryPage;
2229
+
2230
+ if (id === undefined || !isBoundaryId(id)) {
2231
+ return undefined;
2232
+ }
2233
+
2234
+ const rect = element.getBoundingClientRect();
2235
+
2236
+ if (rect.width <= 0 || rect.height <= 0) {
2237
+ return undefined;
2238
+ }
2239
+
2240
+ return {
2241
+ color: boundaryMetadata[id].color,
2242
+ height: rect.height,
2243
+ id,
2244
+ label: labels[id],
2245
+ labelPlacement: rect.top > 28 ? 'above' : 'inside',
2246
+ left: rect.left,
2247
+ top: rect.top,
2248
+ width: rect.width,
2249
+ };
2250
+ })
2251
+ .filter((box): box is BoundaryBox => box !== undefined);
2252
+ }
2253
+
2254
+ function BoundaryOverlay({
2255
+ labels,
2256
+ visible,
2257
+ }: {
2258
+ labels: BoundaryLabels;
2259
+ visible: boolean;
2260
+ }) {
2261
+ const [boxes, setBoxes] = useState<BoundaryBox[]>([]);
2262
+
2263
+ useEffect(() => {
2264
+ if (!visible) {
2265
+ setBoxes([]);
2266
+ return;
2267
+ }
2268
+
2269
+ let animationFrame = 0;
2270
+ const update = () => {
2271
+ cancelAnimationFrame(animationFrame);
2272
+ animationFrame = requestAnimationFrame(() => {
2273
+ setBoxes(collectBoundaryBoxes(labels));
2274
+ });
2275
+ };
2276
+ const observer = new ResizeObserver(update);
2277
+
2278
+ observer.observe(document.body);
2279
+ update();
2280
+ window.addEventListener('resize', update);
2281
+ window.addEventListener('scroll', update, true);
2282
+
2283
+ return () => {
2284
+ cancelAnimationFrame(animationFrame);
2285
+ observer.disconnect();
2286
+ window.removeEventListener('resize', update);
2287
+ window.removeEventListener('scroll', update, true);
2288
+ };
2289
+ }, [labels, visible]);
2290
+
2291
+ if (!visible) {
2292
+ return null;
2293
+ }
2294
+
2295
+ return (
2296
+ <div aria-hidden="true" className="boundary-overlay">
2297
+ {boxes.map((box, index) => (
2298
+ <div
2299
+ className="boundary-overlay__box"
2300
+ data-boundary-id={box.id}
2301
+ data-label-placement={box.labelPlacement}
2302
+ key={\`\${box.id}-\${index}\`}
2303
+ style={{
2304
+ '--boundary-color': box.color,
2305
+ height: box.height,
2306
+ left: box.left,
2307
+ top: box.top,
2308
+ width: box.width,
2309
+ } as CSSProperties}
2310
+ >
2311
+ <span className="boundary-overlay__label">{box.label}</span>
2312
+ </div>
2313
+ ))}
2314
+ </div>
2315
+ );
2316
+ }
2317
+
2318
+ export default function ${toPascalCase(app.id)}Home() {
2319
+ const { i18nInstance, language } = useModernI18n();
2320
+ const t = i18nInstance.t.bind(i18nInstance);
2321
+ const [cart, setCart] = useState<CartState>({});
2322
+ const [showBoundaries, setShowBoundaries] = useState(false);
2323
+ const [effectApiStatus, setEffectApiStatus] = useState('pending');
2324
+ const boundaryLabels = {
2325
+ checkout: t('commerce.boundaries.checkout'),
2326
+ decide: t('commerce.boundaries.decide'),
2327
+ explore: t('commerce.boundaries.explore'),
2328
+ } satisfies BoundaryLabels;
2329
+ const cartLines = products
2330
+ .map(product => ({
2331
+ product,
2332
+ quantity: cart[product.id] ?? 0,
2333
+ }))
2334
+ .filter(line => line.quantity > 0);
2335
+ const cartCount = cartLines.reduce((total, line) => total + line.quantity, 0);
2336
+
2337
+ useEffect(() => {
2338
+ void fetch('${effectApiPrefix(app)}/effect/${effectApiStem(app)}?limit=1', {
2339
+ headers: {
2340
+ accept: 'application/json',
2341
+ },
2342
+ })
2343
+ .then(response => {
2344
+ if (!response.ok) {
2345
+ throw new Error(\`Effect BFF request failed: \${response.status}\`);
2346
+ }
2347
+
2348
+ return response.json() as Promise<{ items?: Array<{ title?: string }> }>;
2349
+ })
2350
+ .then(data => {
2351
+ setEffectApiStatus(data.items[0]?.title ?? 'empty');
2352
+ })
2353
+ .catch(() => {
2354
+ setEffectApiStatus('unavailable');
2355
+ });
2356
+ }, []);
2357
+
2358
+ const addToCart = (id: ProductId) => {
2359
+ setCart(current => ({
2360
+ ...current,
2361
+ [id]: (current[id] ?? 0) + 1,
2362
+ }));
2363
+ };
2364
+
2365
+ const reduceQuantity = (id: ProductId) => {
2366
+ setCart(current => {
2367
+ const quantity = current[id] ?? 0;
2368
+ const next = { ...current };
2369
+
2370
+ if (quantity <= 1) {
2371
+ delete next[id];
2372
+ } else {
2373
+ next[id] = quantity - 1;
2374
+ }
2375
+
2376
+ return next;
2377
+ });
2378
+ };
2379
+
2380
+ const removeFromCart = (id: ProductId) => {
2381
+ setCart(current => {
2382
+ const next = { ...current };
2383
+
2384
+ delete next[id];
2385
+ return next;
2386
+ });
2387
+ };
2388
+
2389
+ return (
2390
+ <main className="commerce-shell">
2391
+ <LocalizedHead />
2392
+ <BoundaryOverlay labels={boundaryLabels} visible={showBoundaries} />
2393
+ <header className="commerce-header" data-boundary="explore">
2394
+ <strong className="commerce-logo">{t('commerce.brand')}</strong>
2395
+ <nav aria-label={t('commerce.navigation.primary')} className="commerce-nav">
2396
+ <a className="commerce-pill" href="#machines">
2397
+ {t('commerce.navigation.machines')}
2398
+ </a>
2399
+ <a className="commerce-pill" href="#checkout">
2400
+ {t('commerce.navigation.checkout')}
2401
+ </a>
2402
+ </nav>
2403
+ <div className="commerce-actions">
2404
+ <a className="commerce-cart-button" data-boundary="checkout" href="#cart">
2405
+ {t('commerce.cart.button', { count: cartCount })}
2406
+ </a>
2407
+ <nav aria-label={t('commerce.language.switcher')} className="commerce-language">
2408
+ {languageCodes.map(code => (
2409
+ <a
2410
+ aria-current={language === code ? 'page' : undefined}
2411
+ className="commerce-pill"
2412
+ href={\`/\${code}\`}
2413
+ key={code}
2414
+ >
2415
+ {t(\`commerce.language.\${code}\`)}
2416
+ </a>
2417
+ ))}
2418
+ </nav>
2419
+ </div>
2420
+ </header>
2421
+
2422
+ <div className="commerce-page">
2423
+ <section className="commerce-product" data-boundary-page="decide" id="machines">
2424
+ <div
2425
+ aria-label={t('commerce.products.fieldLoader.imageAlt')}
2426
+ className="commerce-product-media"
2427
+ role="img"
2428
+ />
2429
+ <div>
2430
+ <p className="commerce-eyebrow">{t('commerce.detail.eyebrow')}</p>
2431
+ <h1 className="commerce-title">{t(featuredProduct.titleKey)}</h1>
2432
+ <p className="commerce-lede">{t(featuredProduct.descriptionKey)}</p>
2433
+ <div className="commerce-facts">
2434
+ <div className="commerce-fact">
2435
+ <span>{t('commerce.detail.price')}</span>
2436
+ <strong>{t(featuredProduct.priceKey)}</strong>
2437
+ </div>
2438
+ <div className="commerce-fact">
2439
+ <span>{t('commerce.detail.power')}</span>
2440
+ <strong>{t(featuredProduct.powerKey)}</strong>
2441
+ </div>
2442
+ <div className="commerce-fact">
2443
+ <span>{t('commerce.detail.availability')}</span>
2444
+ <strong>{t(featuredProduct.availabilityKey)}</strong>
2445
+ </div>
2446
+ </div>
2447
+ <div className="commerce-checkout" data-boundary="checkout" id="checkout">
2448
+ <button
2449
+ className="commerce-button"
2450
+ onClick={() => addToCart(featuredProduct.id)}
2451
+ type="button"
2452
+ >
2453
+ {t('commerce.cart.add')}
2454
+ </button>
2455
+ <a className="commerce-link-button" href="#cart">
2456
+ {t('commerce.cart.view')}
2457
+ </a>
2458
+ </div>
2459
+ </div>
2460
+ </section>
2461
+
2462
+ <section data-boundary="explore">
2463
+ <h2 className="commerce-section-title">{t('commerce.recommendations.title')}</h2>
2464
+ <div className="commerce-grid">
2465
+ {recommendations.map(product => (
2466
+ <article className="commerce-card" key={product.id}>
2467
+ <span>{t(product.badgeKey)}</span>
2468
+ <strong>{t(product.titleKey)}</strong>
2469
+ </article>
2470
+ ))}
2471
+ </div>
2472
+ </section>
2473
+
2474
+ <section className="commerce-cart-panel" data-boundary="checkout" id="cart">
2475
+ <h2>{t('commerce.cart.title')}</h2>
2476
+ {cartLines.length === 0 ? (
2477
+ <p>{t('commerce.cart.empty')}</p>
2478
+ ) : (
2479
+ cartLines.map(line => (
2480
+ <div className="commerce-cart-line" key={line.product.id}>
2481
+ <strong>{t(line.product.titleKey)}</strong>
2482
+ <div className="commerce-quantity">
2483
+ <button
2484
+ aria-label={t('commerce.cart.decrease', {
2485
+ name: t(line.product.titleKey),
2486
+ })}
2487
+ className="commerce-quantity-button"
2488
+ onClick={() => reduceQuantity(line.product.id)}
2489
+ type="button"
2490
+ >
2491
+ -
2492
+ </button>
2493
+ <span>{line.quantity}</span>
2494
+ <button
2495
+ aria-label={t('commerce.cart.increase', {
2496
+ name: t(line.product.titleKey),
2497
+ })}
2498
+ className="commerce-quantity-button"
2499
+ onClick={() => addToCart(line.product.id)}
2500
+ type="button"
2501
+ >
2502
+ +
2503
+ </button>
2504
+ <button
2505
+ className="commerce-link-button"
2506
+ onClick={() => removeFromCart(line.product.id)}
2507
+ type="button"
2508
+ >
2509
+ {t('commerce.cart.remove')}
2510
+ </button>
2511
+ </div>
2512
+ </div>
2513
+ ))
2514
+ )}
2515
+ </section>
2516
+ </div>
2517
+
2518
+ <footer className="commerce-footer" data-boundary="explore">
2519
+ <span>{t('commerce.footer.stack')}</span>
2520
+ <span data-testid="effect-bff-status">{effectApiStatus}</span>
2521
+ <span data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
2522
+ {ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
2523
+ </span>
2524
+ </footer>
2525
+
2526
+ <label className="commerce-boundary-toggle">
2527
+ <input
2528
+ checked={showBoundaries}
2529
+ onChange={event => setShowBoundaries(event.currentTarget.checked)}
2530
+ type="checkbox"
2531
+ />
2532
+ {t('commerce.boundaries.toggle')}
2533
+ </label>
2534
+ </main>
2535
+ );
2536
+ }
2537
+ `;
2538
+ }
1863
2539
  function createLayout(appId) {
1864
2540
  return `import { Outlet } from '@modern-js/plugin-tanstack/runtime';
1865
2541
  import './index.css';
@@ -1894,7 +2570,7 @@ function createAppLocaleMessages(app, language) {
1894
2570
  const czechLabels = {
1895
2571
  commerce: {
1896
2572
  role: 'obchod',
1897
- title: 'Obchodni remote'
2573
+ title: 'Obchodní remote'
1898
2574
  },
1899
2575
  'design-system': {
1900
2576
  role: 'design system',
@@ -1902,20 +2578,20 @@ function createAppLocaleMessages(app, language) {
1902
2578
  },
1903
2579
  identity: {
1904
2580
  role: 'identita',
1905
- title: 'Identitni remote'
2581
+ title: 'Identitní remote'
1906
2582
  }
1907
2583
  };
1908
2584
  if ('shell' === app.kind) return {
1909
2585
  shell: {
1910
2586
  language: {
1911
- cs: 'en' === language ? 'Czech' : 'Cestina',
1912
- en: 'en' === language ? 'English' : 'Anglictina',
2587
+ cs: 'en' === language ? 'Czech' : 'Čeština',
2588
+ en: 'en' === language ? 'English' : 'Angličtina',
1913
2589
  switcher: 'en' === language ? 'Language' : 'Jazyk'
1914
2590
  },
1915
2591
  remotes: {
1916
- commerce: 'en' === language ? 'Commerce Remote' : 'Obchodni remote',
2592
+ commerce: 'en' === language ? 'Commerce Remote' : 'Obchodní remote',
1917
2593
  designSystem: 'en' === language ? 'Design System Remote' : 'Design system remote',
1918
- identity: 'en' === language ? 'Identity Remote' : 'Identitni remote'
2594
+ identity: 'en' === language ? 'Identity Remote' : 'Identitní remote'
1919
2595
  },
1920
2596
  title: 'en' === language ? 'UltraModern SuperApp Shell' : 'UltraModern SuperApp shell'
1921
2597
  }
@@ -1925,11 +2601,74 @@ function createAppLocaleMessages(app, language) {
1925
2601
  role: domain,
1926
2602
  title: `${app.displayName} CZ`
1927
2603
  };
2604
+ if ('commerce' === domain) return {
2605
+ commerce: {
2606
+ boundaries: {
2607
+ checkout: 'en' === language ? 'checkout' : 'pokladna',
2608
+ decide: 'en' === language ? 'decide' : 'rozhodování',
2609
+ explore: 'en' === language ? 'explore' : 'procházení',
2610
+ toggle: 'en' === language ? 'show team boundaries' : 'zobrazit hranice týmů'
2611
+ },
2612
+ brand: 'Acre & Iron',
2613
+ cart: {
2614
+ add: 'en' === language ? 'Add to cart' : 'Přidat do košíku',
2615
+ button: 'en' === language ? 'Your cart ({{count}})' : 'Košík ({{count}})',
2616
+ decrease: 'en' === language ? 'Decrease {{name}} quantity' : 'Snížit množství položky {{name}}',
2617
+ empty: 'en' === language ? 'Your cart is empty.' : 'Košík je prázdný.',
2618
+ increase: 'en' === language ? 'Increase {{name}} quantity' : 'Zvýšit množství položky {{name}}',
2619
+ remove: 'en' === language ? 'Remove' : 'Odebrat',
2620
+ title: 'en' === language ? 'Cart' : 'Košík',
2621
+ view: 'en' === language ? 'View cart' : 'Zobrazit košík'
2622
+ },
2623
+ detail: {
2624
+ availability: 'en' === language ? 'Availability' : 'Dostupnost',
2625
+ eyebrow: 'en' === language ? 'Machine detail' : 'Detail stroje',
2626
+ power: 'en' === language ? 'Power' : 'Výkon',
2627
+ price: 'en' === language ? 'Price' : 'Cena'
2628
+ },
2629
+ footer: {
2630
+ stack: 'en' === language ? 'SPA, SSR-ready Module Federation, React, Effect BFF' : 'SPA, SSR-ready Module Federation, React, Effect BFF'
2631
+ },
2632
+ language: {
2633
+ cs: 'en' === language ? 'Czech' : 'Čeština',
2634
+ en: 'en' === language ? 'English' : 'Angličtina',
2635
+ switcher: 'en' === language ? 'Language' : 'Jazyk'
2636
+ },
2637
+ navigation: {
2638
+ checkout: 'en' === language ? 'Checkout' : 'Pokladna',
2639
+ machines: 'en' === language ? 'Machines' : 'Stroje',
2640
+ primary: 'en' === language ? 'Primary commerce navigation' : 'Hlavní navigace obchodu'
2641
+ },
2642
+ products: {
2643
+ autonomy: {
2644
+ badge: 'en' === language ? 'AI-first option' : 'AI varianta',
2645
+ title: 'en' === language ? 'Autonomy Retrofit Kit' : 'Sada pro autonomní řízení'
2646
+ },
2647
+ fieldLoader: {
2648
+ availability: 'en' === language ? 'In stock' : 'Skladem',
2649
+ description: 'en' === language ? 'A loader-ready tractor for feed, hay, gravel, and winter road work.' : 'Traktor připravený na nakladač pro krmivo, seno, štěrk i zimní údržbu cest.',
2650
+ imageAlt: 'en' === language ? 'Field Loader 112 tractor working on a bright farm lane' : 'Traktor Field Loader 112 pracuje na světlé polní cestě',
2651
+ power: '112 hp',
2652
+ price: 'EUR 42,500',
2653
+ title: 'Field Loader 112'
2654
+ },
2655
+ orchard: {
2656
+ badge: 'en' === language ? 'Best for tight rows' : 'Nejlepší do úzkých řádků',
2657
+ title: 'en' === language ? 'Narrow Orchard Tractor' : 'Úzký sadový traktor'
2658
+ }
2659
+ },
2660
+ recommendations: {
2661
+ title: 'en' === language ? 'Compare alternatives' : 'Porovnat alternativy'
2662
+ },
2663
+ role: 'en' === language ? 'commerce' : 'obchod',
2664
+ title: 'en' === language ? app.displayName : czechLabel.title
2665
+ }
2666
+ };
1928
2667
  return {
1929
2668
  [domain]: {
1930
2669
  language: {
1931
- cs: 'en' === language ? 'Czech' : 'Cestina',
1932
- en: 'en' === language ? 'English' : 'Anglictina',
2670
+ cs: 'en' === language ? 'Czech' : 'Čeština',
2671
+ en: 'en' === language ? 'English' : 'Angličtina',
1933
2672
  switcher: 'en' === language ? 'Language' : 'Jazyk'
1934
2673
  },
1935
2674
  role: 'en' === language ? app.domain ?? app.kind : czechLabel.role,
@@ -2388,7 +3127,7 @@ function createTopology(scope) {
2388
3127
  validation: {
2389
3128
  script: "scripts/validate-ultramodern-workspace.mjs",
2390
3129
  commands: [
2391
- 'pnpm ultramodern:check'
3130
+ 'mise exec -- pnpm ultramodern:check'
2392
3131
  ]
2393
3132
  }
2394
3133
  };
@@ -2619,6 +3358,48 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
2619
3358
  apiSurface: 'effect-bff'
2620
3359
  } : {}
2621
3360
  },
3361
+ ...'commerce' === app.domain ? {
3362
+ boundaryVisualization: {
3363
+ mode: 'overlay',
3364
+ layoutAffecting: false,
3365
+ toggle: 'user-controlled',
3366
+ boundaries: [
3367
+ {
3368
+ id: 'explore',
3369
+ labelKey: 'commerce.boundaries.explore',
3370
+ owner: 'team-explore',
3371
+ color: '#ff5a57',
3372
+ owns: [
3373
+ 'header',
3374
+ 'footer',
3375
+ 'recommendations',
3376
+ 'catalog'
3377
+ ]
3378
+ },
3379
+ {
3380
+ id: 'decide',
3381
+ labelKey: 'commerce.boundaries.decide',
3382
+ owner: 'team-decide',
3383
+ color: '#24d671',
3384
+ owns: [
3385
+ 'product-detail',
3386
+ 'variant-selection'
3387
+ ]
3388
+ },
3389
+ {
3390
+ id: 'checkout',
3391
+ labelKey: 'commerce.boundaries.checkout',
3392
+ owner: 'team-checkout',
3393
+ color: '#f4d044',
3394
+ owns: [
3395
+ 'add-to-cart',
3396
+ 'cart-link',
3397
+ 'cart-lines'
3398
+ ]
3399
+ }
3400
+ ]
3401
+ }
3402
+ } : {},
2622
3403
  ...appHasEffectApi(app) ? {
2623
3404
  effect: {
2624
3405
  runtime: 'effect',
@@ -2643,9 +3424,8 @@ function createGeneratedContract(scope, apps = [
2643
3424
  packageManager: {
2644
3425
  source: 'package.json',
2645
3426
  manager: 'pnpm',
2646
- version: '11.4.0',
2647
- toolchain: 'mise',
2648
- corepack: false
3427
+ version: PNPM_VERSION,
3428
+ toolchain: 'mise'
2649
3429
  },
2650
3430
  versions: {
2651
3431
  typescript: TYPESCRIPT_VERSION,
@@ -2697,6 +3477,7 @@ function createTemplateManifest(modernVersion, packageSource) {
2697
3477
  '.agents/**',
2698
3478
  '.github/**',
2699
3479
  '.gitignore',
3480
+ '.mise.toml',
2700
3481
  '.modernjs/**',
2701
3482
  'AGENTS.md',
2702
3483
  'README.md',
@@ -2771,8 +3552,9 @@ function createTemplateManifest(modernVersion, packageSource) {
2771
3552
  'template-manifest-retained'
2772
3553
  ],
2773
3554
  expectedCommands: [
2774
- 'pnpm install',
2775
- 'pnpm run ultramodern:check'
3555
+ 'mise install',
3556
+ 'mise exec -- pnpm install',
3557
+ 'mise exec -- pnpm run ultramodern:check'
2776
3558
  ]
2777
3559
  }
2778
3560
  };
@@ -2815,9 +3597,7 @@ function writeEffectService(targetDir, scope, packageSource, enableTailwind, ser
2815
3597
  writeFile(targetDir, `${service.directory}/src/modern-app-env.d.ts`, "/// <reference types='@modern-js/app-tools/types' />\n");
2816
3598
  writeFile(targetDir, `${service.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, service));
2817
3599
  writeFile(targetDir, `${service.directory}/src/routes/layout.tsx`, createLayout(service.id));
2818
- writeFile(targetDir, `${service.directory}/src/routes/page.tsx`, `import './index.css';
2819
-
2820
- export default function ${toPascalCase(service.id)}Home() {
3600
+ writeFile(targetDir, `${service.directory}/src/routes/page.tsx`, `export default function ${toPascalCase(service.id)}Home() {
2821
3601
  return <main>${service.id} Effect service</main>;
2822
3602
  }
2823
3603
  `);
@@ -3143,6 +3923,7 @@ function generateUltramodernWorkspace(options) {
3143
3923
  copyRootTemplate(options.targetDir, {
3144
3924
  packageName: options.packageName,
3145
3925
  packageScope: scope,
3926
+ pnpmVersion: PNPM_VERSION,
3146
3927
  tailwindEnabled: String(enableTailwind)
3147
3928
  });
3148
3929
  writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
@@ -3171,6 +3952,7 @@ const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
3171
3952
  const src_TANSTACK_ROUTER_VERSION = '1.170.8';
3172
3953
  const src_TAILWIND_VERSION = '4.3.0';
3173
3954
  const src_TAILWIND_POSTCSS_VERSION = '4.3.0';
3955
+ const src_PNPM_VERSION = '11.4.0';
3174
3956
  const requiredDeniedPaths = [
3175
3957
  '.git/**',
3176
3958
  '.npmrc',
@@ -3411,9 +4193,10 @@ function createBuiltinTemplateManifest(version) {
3411
4193
  'template-manifest-retained'
3412
4194
  ],
3413
4195
  expectedCommands: [
3414
- 'pnpm install',
3415
- 'pnpm test',
3416
- 'pnpm run ultramodern:check'
4196
+ 'mise install',
4197
+ 'mise exec -- pnpm install',
4198
+ 'mise exec -- pnpm test',
4199
+ 'mise exec -- pnpm run ultramodern:check'
3417
4200
  ]
3418
4201
  }
3419
4202
  };
@@ -3786,7 +4569,8 @@ async function main() {
3786
4569
  const dim = '\x1b[2m\x1b[3m';
3787
4570
  const reset = '\x1b[0m';
3788
4571
  console.log(`${i18n.t(localeKeys.message.success)}\n`);
3789
- console.log(`${dim} pnpm ultramodern:check${reset}\n`);
4572
+ console.log(`${dim} mise install${reset}`);
4573
+ console.log(`${dim} mise exec -- pnpm ultramodern:check${reset}\n`);
3790
4574
  return;
3791
4575
  }
3792
4576
  if (node_fs.existsSync(targetDir)) {
@@ -3814,8 +4598,9 @@ async function main() {
3814
4598
  if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
3815
4599
  projectName
3816
4600
  })}${reset}`);
4601
+ console.log(`${dim} mise install${reset}`);
3817
4602
  console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
3818
- console.log(`${dim} pnpm ultramodern:check${reset}`);
4603
+ console.log(`${dim} mise exec -- pnpm ultramodern:check${reset}`);
3819
4604
  console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
3820
4605
  return;
3821
4606
  }
@@ -3841,6 +4626,7 @@ async function main() {
3841
4626
  tanstackRouterVersion: src_TANSTACK_ROUTER_VERSION,
3842
4627
  tailwindVersion: src_TAILWIND_VERSION,
3843
4628
  tailwindPostcssVersion: src_TAILWIND_POSTCSS_VERSION,
4629
+ pnpmVersion: src_PNPM_VERSION,
3844
4630
  isSubproject,
3845
4631
  routerFramework,
3846
4632
  bffRuntime,
@@ -3935,6 +4721,7 @@ function copyTemplate(src, dest, options) {
3935
4721
  tanstackRouterVersion: options.tanstackRouterVersion,
3936
4722
  tailwindVersion: options.tailwindVersion,
3937
4723
  tailwindPostcssVersion: options.tailwindPostcssVersion,
4724
+ pnpmVersion: options.pnpmVersion,
3938
4725
  isSubproject: options.isSubproject,
3939
4726
  isTanstackRouter: 'tanstack' === options.routerFramework,
3940
4727
  enableBff: 'none' !== options.bffRuntime,