@atelic-action/ui 0.17.2 → 1.0.0

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 (184) hide show
  1. package/README.md +428 -44
  2. package/bin/atelic-check-base-path.ts +5 -0
  3. package/bin/atelic-clean-sitemap.ts +5 -0
  4. package/bin/atelic-contact-canary.ts +5 -0
  5. package/bin/atelic-print-sheets.sh +1 -1
  6. package/bin/atelic-seal-document.mjs +1 -1
  7. package/bin/atelic-serve-static.ts +1 -1
  8. package/dist/gate/index.d.ts +1 -1
  9. package/dist/gate/index.js +1 -1
  10. package/dist/gate/middleware.d.ts +4 -1
  11. package/dist/gate/middleware.js +14 -5
  12. package/dist/gate/wall.d.ts +25 -0
  13. package/dist/gate/wall.js +41 -2
  14. package/dist/routing/basePath.d.ts +24 -0
  15. package/dist/routing/basePath.js +54 -0
  16. package/dist/vite/index.d.ts +3 -2
  17. package/dist/vite/index.js +3 -2
  18. package/dist/vite/marketing-config.d.ts +50 -0
  19. package/dist/vite/marketing-config.js +83 -0
  20. package/package.json +164 -5
  21. package/src/app/AppProvider.tsx +41 -0
  22. package/src/app/AuthGuard.tsx +27 -0
  23. package/src/app/CardErrorBoundary.tsx +39 -0
  24. package/src/app/ErrorFallback.tsx +39 -0
  25. package/src/app/GoogleGlyph.tsx +22 -0
  26. package/src/app/config.ts +54 -0
  27. package/src/app/configure.ts +18 -0
  28. package/src/app/context/AppContext.tsx +52 -0
  29. package/src/app/context/SidebarContext.tsx +124 -0
  30. package/src/app/context/UserContext.tsx +139 -0
  31. package/src/app/hooks/useDebounce.ts +12 -0
  32. package/src/app/hooks/useIsDesktop.ts +31 -0
  33. package/src/app/hooks/useWakeLock.ts +49 -0
  34. package/src/app/index.ts +64 -0
  35. package/src/app/layout/ActionButton.tsx +67 -0
  36. package/src/app/layout/AppLayout.tsx +110 -0
  37. package/src/app/layout/AppPage.tsx +268 -0
  38. package/src/app/layout/Breadcrumbs.tsx +57 -0
  39. package/src/app/layout/DesktopRail.tsx +168 -0
  40. package/src/app/layout/FormActionButtons.tsx +69 -0
  41. package/src/app/layout/MobileMenu.tsx +156 -0
  42. package/src/app/layout/MobileTabBar.tsx +43 -0
  43. package/src/app/layout/PageError.tsx +30 -0
  44. package/src/app/layout/PageSkeleton.tsx +65 -0
  45. package/src/app/layout/SectionCards.tsx +33 -0
  46. package/src/app/layout/UserMenu.tsx +104 -0
  47. package/src/app/layout/identity.ts +22 -0
  48. package/src/app/layout/index.ts +20 -0
  49. package/src/app/layout/nav.ts +52 -0
  50. package/src/app/lib/dates.ts +57 -0
  51. package/src/app/lib/env.ts +37 -0
  52. package/src/app/lib/format.ts +61 -0
  53. package/src/app/lib/http.ts +205 -0
  54. package/src/app/lib/lastAuth.ts +45 -0
  55. package/src/app/lib/oauth.ts +32 -0
  56. package/src/app/lib/publicRoutes.ts +33 -0
  57. package/src/app/lib/queryClient.ts +38 -0
  58. package/src/app/lib/search.ts +17 -0
  59. package/src/app/lib/session.ts +36 -0
  60. package/src/app/lib/tokenRefresh.ts +137 -0
  61. package/src/app/lib/transformKeys.ts +60 -0
  62. package/src/app/lib/units.ts +228 -0
  63. package/src/app/lib/unwrap.ts +48 -0
  64. package/src/app/lib/utils.ts +10 -0
  65. package/src/app/stores/authStore.ts +80 -0
  66. package/src/app/stores/storage.ts +40 -0
  67. package/src/app/stores/unitsStore.ts +54 -0
  68. package/src/app/types/index.ts +72 -0
  69. package/src/app/types/query.ts +11 -0
  70. package/src/app/ui/accordion.tsx +54 -0
  71. package/src/app/ui/alert-dialog.tsx +114 -0
  72. package/src/app/ui/alert.tsx +49 -0
  73. package/src/app/ui/avatar.tsx +45 -0
  74. package/src/app/ui/badge-list.tsx +51 -0
  75. package/src/app/ui/badge.tsx +33 -0
  76. package/src/app/ui/button.tsx +49 -0
  77. package/src/app/ui/callout.tsx +22 -0
  78. package/src/app/ui/card.tsx +55 -0
  79. package/src/app/ui/checkbox.tsx +26 -0
  80. package/src/app/ui/clear-button.tsx +42 -0
  81. package/src/app/ui/command.tsx +140 -0
  82. package/src/app/ui/country-dropdown.tsx +184 -0
  83. package/src/app/ui/dialog.tsx +104 -0
  84. package/src/app/ui/dropdown-menu.tsx +185 -0
  85. package/src/app/ui/form.tsx +172 -0
  86. package/src/app/ui/index.ts +36 -0
  87. package/src/app/ui/input.tsx +22 -0
  88. package/src/app/ui/label.tsx +19 -0
  89. package/src/app/ui/option-select.tsx +56 -0
  90. package/src/app/ui/pagination.tsx +90 -0
  91. package/src/app/ui/popover.tsx +29 -0
  92. package/src/app/ui/progress.tsx +25 -0
  93. package/src/app/ui/search-input.tsx +63 -0
  94. package/src/app/ui/select.tsx +151 -0
  95. package/src/app/ui/separator.tsx +24 -0
  96. package/src/app/ui/sheet.tsx +119 -0
  97. package/src/app/ui/slider.tsx +23 -0
  98. package/src/app/ui/switch.tsx +27 -0
  99. package/src/app/ui/textarea.tsx +21 -0
  100. package/src/app/ui/tooltip.tsx +32 -0
  101. package/src/artifact/ArtifactShell.tsx +2 -2
  102. package/src/artifact/Gate.tsx +2 -2
  103. package/src/artifact/Writeup.tsx +7 -7
  104. package/src/chrome/SiteShell.tsx +141 -0
  105. package/src/chrome/index.ts +2 -0
  106. package/src/components/Disclosure.tsx +80 -14
  107. package/src/components/GateLogin.tsx +1 -1
  108. package/src/components/Watermark.tsx +1 -1
  109. package/src/components/index.ts +6 -2
  110. package/src/gate/index.ts +1 -1
  111. package/src/gate/middleware.ts +17 -6
  112. package/src/gate/wall.ts +60 -2
  113. package/src/lib/basePathAudit.ts +96 -0
  114. package/src/lib/displayDate.ts +25 -0
  115. package/src/lib/honeypot.ts +11 -0
  116. package/src/lib/hours.ts +28 -0
  117. package/src/lib/sitemap.ts +34 -0
  118. package/src/lib/trackEvent.ts +26 -0
  119. package/src/lib/useOpenHeight.ts +28 -0
  120. package/src/primitives/Button.tsx +3 -1
  121. package/src/primitives/Chip.tsx +3 -1
  122. package/src/primitives/ImageFrame.tsx +48 -0
  123. package/src/primitives/ProseBlocks.tsx +67 -0
  124. package/src/primitives/QuoteCard.tsx +20 -0
  125. package/src/primitives/StarRow.tsx +37 -0
  126. package/src/primitives/TickList.tsx +24 -0
  127. package/src/primitives/index.ts +5 -0
  128. package/src/routing/BasePathProvider.tsx +47 -0
  129. package/src/routing/basePath.ts +57 -0
  130. package/src/routing/index.ts +8 -0
  131. package/src/scripts/check-base-path.ts +130 -0
  132. package/src/scripts/clean-sitemap.ts +27 -0
  133. package/src/scripts/contact-canary.ts +98 -0
  134. package/src/scripts/serve-static.ts +29 -3
  135. package/src/sections/BlogIndex.tsx +57 -0
  136. package/src/sections/BlogPost.tsx +65 -0
  137. package/src/sections/CTABanner.tsx +93 -0
  138. package/src/sections/CTASection.tsx +60 -0
  139. package/src/sections/ComparisonTable.tsx +114 -0
  140. package/src/sections/ContactForm.tsx +163 -0
  141. package/src/sections/ContactInfoCard.tsx +92 -0
  142. package/src/sections/CredentialList.tsx +28 -0
  143. package/src/sections/DeviceShowcase.tsx +39 -0
  144. package/src/sections/ExpandList.tsx +58 -0
  145. package/src/sections/FAQBrowser.tsx +283 -0
  146. package/src/sections/FeatureDetail.tsx +60 -0
  147. package/src/sections/FeatureGrid.tsx +83 -0
  148. package/src/sections/Gallery.tsx +84 -0
  149. package/src/sections/Hero.tsx +110 -0
  150. package/src/sections/HoursTable.tsx +23 -0
  151. package/src/sections/JumpRail.tsx +35 -0
  152. package/src/sections/LegalPage.tsx +43 -0
  153. package/src/sections/LocationSection.tsx +66 -0
  154. package/src/sections/Locations.tsx +71 -0
  155. package/src/sections/LogoMarquee.tsx +27 -0
  156. package/src/sections/MapEmbed.tsx +75 -0
  157. package/src/sections/MediaBand.tsx +20 -0
  158. package/src/sections/MenuList.tsx +44 -0
  159. package/src/sections/PageHero.tsx +25 -0
  160. package/src/sections/PressQuotes.tsx +51 -0
  161. package/src/sections/PricingBand.tsx +43 -0
  162. package/src/sections/PricingCard.tsx +36 -0
  163. package/src/sections/ProcessSteps.tsx +45 -0
  164. package/src/sections/ProfileCards.tsx +73 -0
  165. package/src/sections/RevealSlider.tsx +156 -0
  166. package/src/sections/ReviewCarousel.tsx +223 -0
  167. package/src/sections/ReviewWall.tsx +220 -0
  168. package/src/sections/SocialStrip.tsx +83 -0
  169. package/src/sections/SplitSection.tsx +56 -0
  170. package/src/sections/index.ts +76 -0
  171. package/src/sections/siteConfig.ts +201 -0
  172. package/src/styles/app.css +673 -0
  173. package/src/styles/artifact.css +472 -399
  174. package/src/styles/base.css +49 -47
  175. package/src/styles/chrome.css +71 -71
  176. package/src/styles/components.css +150 -140
  177. package/src/styles/gate.css +29 -29
  178. package/src/styles/primitives.css +190 -25
  179. package/src/styles/sections.css +2652 -0
  180. package/src/styles/star-rating.css +2 -2
  181. package/src/vite/index.ts +9 -2
  182. package/src/vite/marketing-config.ts +128 -0
  183. package/src/components/Collapsible.tsx +0 -43
  184. /package/src/scripts/{build-gate-payload.mjs → seal-document.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  /* StarRating: a row of stars in half star steps. On its own sheet, and not
2
- under .mkt, so an app that wears none of the site chrome can import this
3
- file alone; components.css imports it for every site.
2
+ scoped under .atelic-ui, so an app that wears none of the site chrome can
3
+ import this file alone; components.css imports it for every site.
4
4
 
5
5
  The two colors are custom properties with fallbacks off the text color, so
6
6
  the row needs no theme token and takes a color from a prop, from a site's
package/src/vite/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * What a site's vite.config.ts imports (`@atelic-action/ui/vite`): the
3
- * configuration of an artifact site as one call, and the plugin inside it
4
- * for a site that writes its own.
3
+ * configuration of an artifact site or a marketing site as one call, and the
4
+ * plugin inside the artifact one for a site that writes its own.
5
5
  */
6
6
  export {
7
7
  type ArtifactConfigOptions,
@@ -9,4 +9,11 @@ export {
9
9
  artifactConfig,
10
10
  artifactPages,
11
11
  } from "./artifact-config.js";
12
+ export {
13
+ type MarketingConfigOptions,
14
+ type MarketingPage,
15
+ marketingBasePath,
16
+ marketingConfig,
17
+ NOT_FOUND_PAGE,
18
+ } from "./marketing-config.js";
12
19
  export { DOC_DIR, isPrivateContent, privateChunks } from "./private-chunks.js";
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The whole Vite configuration of a marketing site, as one call. Every site
3
+ * cut from template-marketing carried its own copy of this configuration;
4
+ * here it is written once (0.19.0, ATE-643), and a site's vite.config.ts
5
+ * hands over its config and the pages the crawler cannot find:
6
+ *
7
+ * import { marketingConfig } from "@atelic-action/ui/vite";
8
+ * import { site } from "./src/site.config";
9
+ *
10
+ * export default marketingConfig({ site, pages: [{ path: "/blog/first-post" }] });
11
+ *
12
+ * The site's own paths (the `@` alias to its src) are found from the
13
+ * directory Vite is run in, which is the site's root.
14
+ */
15
+ import { resolve } from "node:path";
16
+ import { tanstackStart } from "@tanstack/react-start/plugin/vite";
17
+ import react from "@vitejs/plugin-react";
18
+ import { mergeConfig, type UserConfig } from "vite";
19
+ import { normalizeBasePath } from "../routing/basePath.js";
20
+
21
+ /** One entry of the prerender list, as TanStack Start takes it. */
22
+ export interface MarketingPage {
23
+ path: string;
24
+ prerender?: { enabled: boolean };
25
+ sitemap?: { exclude: boolean };
26
+ }
27
+
28
+ export interface MarketingConfigOptions {
29
+ /**
30
+ * The two keys of the site's config the build reads: `url`, the canonical
31
+ * URL (with the base path on it for a based cut), and `basePath`.
32
+ */
33
+ site: { url: string; basePath?: string };
34
+ /**
35
+ * Pages the link crawler cannot reach on its own: a post on a dynamic
36
+ * route, a page nothing links to. The 404 page is always added.
37
+ */
38
+ pages?: readonly MarketingPage[];
39
+ /**
40
+ * The host the sitemap's URLs carry. Defaults to the origin of `site.url`.
41
+ */
42
+ sitemapHost?: string;
43
+ /**
44
+ * The escape hatch for a site with a real need of its own: merged over
45
+ * the result with Vite's `mergeConfig`.
46
+ */
47
+ extend?: UserConfig;
48
+ /** The environment to read `VITE_BASE_PATH` from; a test passes its own. */
49
+ env?: Record<string, string | undefined>;
50
+ }
51
+
52
+ /** The 404 page, prerendered and copied to 404.html by the build, never in the sitemap. */
53
+ export const NOT_FOUND_PAGE: MarketingPage = {
54
+ path: "/404",
55
+ prerender: { enabled: true },
56
+ sitemap: { exclude: true },
57
+ };
58
+
59
+ /**
60
+ * The base the build uses: `VITE_BASE_PATH` when set (a rehearsal build,
61
+ * which CI runs under "/proto"), the config's `basePath` otherwise. Throws
62
+ * when a based config's `url` does not end with its base, since canonical and
63
+ * Open Graph URLs are built from it.
64
+ */
65
+ export function marketingBasePath(
66
+ site: { url: string; basePath?: string },
67
+ env: Record<string, string | undefined> = process.env,
68
+ ): string {
69
+ const override = env.VITE_BASE_PATH;
70
+ const basePath = normalizeBasePath(override ?? site.basePath);
71
+ if (override === undefined && basePath && !site.url.endsWith(basePath)) {
72
+ throw new Error(
73
+ `site.url (${site.url}) must end with basePath (${basePath}): canonical and Open Graph URLs are built from it`,
74
+ );
75
+ }
76
+ return basePath;
77
+ }
78
+
79
+ export function marketingConfig({
80
+ site,
81
+ pages = [],
82
+ sitemapHost,
83
+ extend,
84
+ env = process.env,
85
+ }: MarketingConfigOptions): UserConfig {
86
+ const basePath = marketingBasePath(site, env);
87
+ const listed = pages.filter((page) => page.path !== NOT_FOUND_PAGE.path);
88
+ const config: UserConfig = {
89
+ // The base becomes Vite's `base`, so every built asset URL and every
90
+ // rebased `url(/...)` in the CSS carries it, and the router's
91
+ // `basepath`, so the router and the prerender both work under it.
92
+ base: `${basePath}/`,
93
+ // No `build.cssCodeSplit: false`. Every sheet a site carries, the
94
+ // package's included, rides in the one base sheet its root route links
95
+ // (src/styles/base.css), so no component imports a stylesheet and there
96
+ // is no component bundle to fold. With the flag off, TanStack Start
97
+ // looks for the `style.css` that folding emits, finds none, and fails
98
+ // the build (ATE-643, as artifactConfig found first). A site that gives
99
+ // a component its own stylesheet again sets the flag through `extend`.
100
+ plugins: [
101
+ tanstackStart({
102
+ ...(basePath ? { router: { basepath: basePath } } : {}),
103
+ prerender: { enabled: true, crawlLinks: true },
104
+ pages: [NOT_FOUND_PAGE, ...listed],
105
+ sitemap: {
106
+ // A based cut is a preview under another project's host, noindex
107
+ // by that host's header rule, so it ships no sitemap.
108
+ enabled: !basePath,
109
+ host: sitemapHost ?? new URL(site.url).origin,
110
+ },
111
+ }),
112
+ react(),
113
+ ],
114
+ resolve: {
115
+ alias: {
116
+ // The site's src, from the directory Vite is run in: this file
117
+ // lives in the package, so nothing here can be found relative to it.
118
+ "@": resolve(process.cwd(), "./src"),
119
+ },
120
+ },
121
+ // @atelic-action/ui ships TSX and CSS as written, so the prerender's
122
+ // server build has to compile it rather than hand it to Node.
123
+ ssr: {
124
+ noExternal: ["@atelic-action/ui"],
125
+ },
126
+ };
127
+ return extend ? mergeConfig(config, extend) : config;
128
+ }
@@ -1,43 +0,0 @@
1
- import { ChevronDown } from "lucide-react";
2
- import type { ReactNode } from "react";
3
-
4
- export interface CollapsibleProps {
5
- /** What is inside, e.g. "Your Main Pages". Always visible. */
6
- label: ReactNode;
7
- /**
8
- * What the detail shows, in a sentence, so a reader who never opens it
9
- * still has the point and the detail needs no explaining once open.
10
- */
11
- summary?: ReactNode;
12
- /** Open on first render; closed by default. */
13
- defaultOpen?: boolean;
14
- className?: string;
15
- children: ReactNode;
16
- }
17
-
18
- /**
19
- * A labeled line the reader opens to see the detail behind it. It is a
20
- * native details element, so it opens with a click, a tap, Enter, or Space,
21
- * works before any script loads, and a browser's find in page opens it. It
22
- * prints open.
23
- */
24
- export function Collapsible({
25
- label,
26
- summary,
27
- defaultOpen,
28
- className,
29
- children,
30
- }: CollapsibleProps) {
31
- return (
32
- <details className={["collapsible", className].filter(Boolean).join(" ")} open={defaultOpen}>
33
- <summary>
34
- <span className="collapsible-text">
35
- <span className="collapsible-label">{label}</span>
36
- {summary && <span className="collapsible-summary">{summary}</span>}
37
- </span>
38
- <ChevronDown className="collapsible-chevron" size={18} aria-hidden="true" />
39
- </summary>
40
- <div className="collapsible-body">{children}</div>
41
- </details>
42
- );
43
- }