@woodsportal/hubspot-kit 1.0.4 → 1.0.26

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 (217) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +4 -2
  3. package/dist/{chunk-JDGIAYHX.js → chunk-GTTIJVAR.js} +43 -3
  4. package/dist/chunk-GTTIJVAR.js.map +1 -0
  5. package/dist/cli.js +1400 -338
  6. package/dist/cli.js.map +1 -1
  7. package/dist/exec-U2OJATR3.js +12 -0
  8. package/dist/index.d.ts +20 -0
  9. package/dist/index.js +8 -1
  10. package/dist/index.js.map +1 -1
  11. package/examples/greenfield/module-hybrid-cdn/README.md +40 -3
  12. package/examples/greenfield/module-hybrid-cdn/public/fields.json +44 -1
  13. package/examples/greenfield/module-hybrid-cdn/src/app.tsx +8 -0
  14. package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
  15. package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
  16. package/examples/greenfield/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
  17. package/examples/greenfield/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
  18. package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
  19. package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
  20. package/examples/greenfield/module-hybrid-cdn/src/config/router.ts +4 -0
  21. package/examples/greenfield/module-hybrid-cdn/src/config/routes.ts +5 -0
  22. package/examples/greenfield/module-hybrid-cdn/src/defaultData.ts +2 -7
  23. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
  24. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
  25. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
  26. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
  27. package/examples/greenfield/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
  28. package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +47 -0
  29. package/examples/greenfield/module-hybrid-cdn/src/features/starter/index.ts +6 -0
  30. package/examples/greenfield/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
  31. package/examples/greenfield/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
  32. package/examples/greenfield/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
  33. package/examples/greenfield/module-hybrid-cdn/src/main.tsx +4 -3
  34. package/examples/greenfield/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
  35. package/examples/greenfield/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
  36. package/examples/greenfield/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
  37. package/examples/greenfield/module-hybrid-cdn/src/routes/__root.tsx +15 -0
  38. package/examples/greenfield/module-hybrid-cdn/src/routes/guide.tsx +7 -0
  39. package/examples/greenfield/module-hybrid-cdn/src/routes/index.tsx +7 -0
  40. package/examples/greenfield/module-hybrid-cdn/tsconfig.json +20 -0
  41. package/examples/greenfield/module-monolith/public/fields.json +44 -1
  42. package/examples/greenfield/module-monolith/src/app.tsx +8 -0
  43. package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +8 -0
  44. package/examples/greenfield/module-monolith/src/components/layout/section-card.tsx +20 -0
  45. package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +28 -0
  46. package/examples/greenfield/module-monolith/src/components/layout/starter-shell.tsx +18 -0
  47. package/examples/greenfield/module-monolith/src/config/router.ts +4 -0
  48. package/examples/greenfield/module-monolith/src/config/routes.ts +5 -0
  49. package/examples/greenfield/module-monolith/src/defaultData.ts +2 -7
  50. package/examples/greenfield/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
  51. package/examples/greenfield/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
  52. package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
  53. package/examples/greenfield/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
  54. package/examples/greenfield/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
  55. package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +47 -0
  56. package/examples/greenfield/module-monolith/src/features/starter/index.ts +6 -0
  57. package/examples/greenfield/module-monolith/src/hubspot/default-data.ts +7 -0
  58. package/examples/greenfield/module-monolith/src/hubspot/types.ts +17 -0
  59. package/examples/greenfield/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
  60. package/examples/greenfield/module-monolith/src/main.tsx +5 -0
  61. package/examples/greenfield/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
  62. package/examples/greenfield/module-monolith/src/portal/mount-portal.tsx +12 -0
  63. package/examples/greenfield/module-monolith/src/routeTree.gen.ts +77 -0
  64. package/examples/greenfield/module-monolith/src/routes/__root.tsx +15 -0
  65. package/examples/greenfield/module-monolith/src/routes/guide.tsx +7 -0
  66. package/examples/greenfield/module-monolith/src/routes/index.tsx +7 -0
  67. package/examples/greenfield/module-monolith/tsconfig.json +20 -0
  68. package/package.json +4 -1
  69. package/scripts/check-hubspot-bundle-size.mjs +5 -2
  70. package/scripts/ensure-wphs-workspace.mjs +2 -1
  71. package/scripts/hubspot-module-postbuild.mjs +1 -1
  72. package/scripts/read-woodsportal-config.mjs +4 -1
  73. package/scripts/run-hubspot-module-postbuild.mjs +9 -4
  74. package/scripts/vite/bootstrap-shim-resolver.js +47 -0
  75. package/scripts/vite/ensure-dev-kit-shims.js +7 -1
  76. package/scripts/vite/react-dev-plugin-options.js +2 -1
  77. package/scripts/watch-hubspot.mjs +107 -17
  78. package/scripts/wphs-paths.mjs +1 -0
  79. package/scripts/write-audit-artifacts.mjs +2 -1
  80. package/src/dev/bootstrap-greenfield.tsx +59 -0
  81. package/src/dev/bootstrap.tsx +46 -0
  82. package/src/dev-module-config/__tests__/fixtures/fields.json +3014 -0
  83. package/src/dev-module-config/__tests__/fixtures/golden-module.fixture.ts +102 -0
  84. package/src/dev-module-config/__tests__/module-config-storage.test.ts +40 -0
  85. package/src/dev-module-config/__tests__/module-config-transform.test.ts +55 -0
  86. package/src/dev-module-config/__tests__/normalize-repeater-items.test.ts +46 -0
  87. package/src/dev-module-config/dev-module-display-name.ts +5 -0
  88. package/src/dev-module-config/runtime/module-config-bridge.ts +27 -0
  89. package/src/dev-module-config/runtime/module-config-runtime.ts +185 -0
  90. package/src/dev-module-config/schema/build-default-item.ts +7 -0
  91. package/src/dev-module-config/schema/deep-merge.ts +27 -0
  92. package/src/dev-module-config/schema/extract-defaults.ts +62 -0
  93. package/src/dev-module-config/schema/normalize-choices.ts +18 -0
  94. package/src/dev-module-config/schema/normalize-repeater-items.ts +31 -0
  95. package/src/dev-module-config/schema/parse-fields-json.ts +2 -0
  96. package/src/dev-module-config/schema/types.ts +75 -0
  97. package/src/dev-module-config/schema/validate-module-config.ts +81 -0
  98. package/src/dev-module-config/schema/visibility.ts +130 -0
  99. package/src/dev-module-config/storage/config-json.ts +46 -0
  100. package/src/dev-module-config/storage/workspace-store.ts +139 -0
  101. package/src/dev-module-config/transform/build-hubspot-data.ts +409 -0
  102. package/src/dev-module-config/ui/FieldRenderer.tsx +353 -0
  103. package/src/dev-module-config/ui/ModuleConfigAnimatedPresence.tsx +72 -0
  104. package/src/dev-module-config/ui/ModuleConfigCollapsible.tsx +43 -0
  105. package/src/dev-module-config/ui/ModuleConfigDockSideIcon.tsx +27 -0
  106. package/src/dev-module-config/ui/ModuleConfigOverlay.tsx +134 -0
  107. package/src/dev-module-config/ui/ModuleConfigPersistenceStatus.tsx +89 -0
  108. package/src/dev-module-config/ui/ModuleConfigShell.tsx +355 -0
  109. package/src/dev-module-config/ui/ModuleConfigThemeToggle.tsx +32 -0
  110. package/src/dev-module-config/ui/ModuleConfigUiContext.tsx +65 -0
  111. package/src/dev-module-config/ui/editors/DeveloperOptionsEditor.tsx +61 -0
  112. package/src/dev-module-config/ui/editors/MenuOptionsEditor.tsx +103 -0
  113. package/src/dev-module-config/ui/hubspot/HsColorField.tsx +106 -0
  114. package/src/dev-module-config/ui/hubspot/HsColorPickerPopover.tsx +210 -0
  115. package/src/dev-module-config/ui/hubspot/HsFieldLabel.tsx +24 -0
  116. package/src/dev-module-config/ui/hubspot/HsFieldWidgets.tsx +248 -0
  117. package/src/dev-module-config/ui/hubspot/HsImageField.tsx +112 -0
  118. package/src/dev-module-config/ui/hubspot/HsOccurrenceGroupEditor.tsx +243 -0
  119. package/src/dev-module-config/ui/hubspot/HsRepeaterList.tsx +178 -0
  120. package/src/dev-module-config/ui/hubspot/HsSectionHeader.tsx +20 -0
  121. package/src/dev-module-config/ui/hubspot/HsToggle.tsx +39 -0
  122. package/src/dev-module-config/ui/hubspot/HsToggleRow.tsx +30 -0
  123. package/src/dev-module-config/ui/hubspot/hs-color-palette.ts +11 -0
  124. package/src/dev-module-config/ui/hubspot/module-config-typography.ts +26 -0
  125. package/src/dev-module-config/ui/module-config-motion.css +134 -0
  126. package/src/dev-module-config/ui/module-config-motion.ts +28 -0
  127. package/src/dev-module-config/ui/module-config-overlay-side.ts +29 -0
  128. package/src/dev-module-config/ui/module-config-surfaces.ts +27 -0
  129. package/src/dev-module-config/ui/module-config-theme.css +489 -0
  130. package/src/dev-module-config/ui/module-config-toolbar-theme.ts +35 -0
  131. package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +194 -0
  132. package/src/dev-module-config/ui/use-dev-module-config-live-revision.ts +22 -0
  133. package/src/dev-module-config/ui/use-hubspot-live-exports.ts +27 -0
  134. package/src/dev-module-config/ui/use-module-config.ts +124 -0
  135. package/src/routing/build-api-routes.ts +37 -0
  136. package/src/routing/route-format.ts +19 -0
  137. package/templates/module-hybrid-cdn/README.md +53 -3
  138. package/templates/module-hybrid-cdn/index.html +12 -1
  139. package/templates/module-hybrid-cdn/package.json +6 -2
  140. package/templates/module-hybrid-cdn/public/fields.json +44 -1
  141. package/templates/module-hybrid-cdn/src/app.tsx +8 -0
  142. package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
  143. package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
  144. package/templates/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
  145. package/templates/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
  146. package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
  147. package/templates/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
  148. package/templates/module-hybrid-cdn/src/config/router.ts +4 -0
  149. package/templates/module-hybrid-cdn/src/config/routes.ts +5 -0
  150. package/templates/module-hybrid-cdn/src/defaultData.ts +2 -7
  151. package/templates/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
  152. package/templates/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
  153. package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
  154. package/templates/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
  155. package/templates/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
  156. package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +42 -0
  157. package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
  158. package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +30 -0
  159. package/templates/module-hybrid-cdn/src/features/starter/index.ts +6 -0
  160. package/templates/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
  161. package/templates/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
  162. package/templates/module-hybrid-cdn/src/hubspot/module-entry.tsx +9 -0
  163. package/templates/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
  164. package/templates/module-hybrid-cdn/src/main.tsx +4 -3
  165. package/templates/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
  166. package/templates/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
  167. package/templates/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
  168. package/templates/module-hybrid-cdn/src/routes/__root.tsx +15 -0
  169. package/templates/module-hybrid-cdn/src/routes/guide.tsx +7 -0
  170. package/templates/module-hybrid-cdn/src/routes/index.tsx +7 -0
  171. package/templates/module-hybrid-cdn/tsconfig.json +20 -0
  172. package/templates/module-hybrid-cdn/vite.config.monolith.mjs +8 -0
  173. package/templates/module-monolith/README.md +59 -0
  174. package/templates/module-monolith/index.html +12 -0
  175. package/templates/module-monolith/package.json +10 -3
  176. package/templates/module-monolith/public/fields.json +44 -1
  177. package/templates/module-monolith/public/module.html +1 -0
  178. package/templates/module-monolith/src/app.tsx +8 -0
  179. package/templates/module-monolith/src/assets/css/main.dev.css +6 -1
  180. package/templates/module-monolith/src/components/layout/section-card.tsx +20 -0
  181. package/templates/module-monolith/src/components/layout/starter-nav.tsx +28 -0
  182. package/templates/module-monolith/src/components/layout/starter-shell.tsx +18 -0
  183. package/templates/module-monolith/src/config/router.ts +4 -0
  184. package/templates/module-monolith/src/config/routes.ts +5 -0
  185. package/templates/module-monolith/src/defaultData.ts +2 -7
  186. package/templates/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
  187. package/templates/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
  188. package/templates/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
  189. package/templates/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
  190. package/templates/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
  191. package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +42 -0
  192. package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
  193. package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.ts +30 -0
  194. package/templates/module-monolith/src/features/starter/index.ts +6 -0
  195. package/templates/module-monolith/src/hubspot/default-data.ts +7 -0
  196. package/templates/module-monolith/src/hubspot/module-entry.tsx +9 -0
  197. package/templates/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
  198. package/templates/module-monolith/src/main.tsx +4 -3
  199. package/templates/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
  200. package/templates/module-monolith/src/portal/mount-portal.tsx +12 -0
  201. package/templates/module-monolith/src/routeTree.gen.ts +77 -0
  202. package/templates/module-monolith/src/routes/__root.tsx +15 -0
  203. package/templates/module-monolith/src/routes/guide.tsx +7 -0
  204. package/templates/module-monolith/src/routes/index.tsx +7 -0
  205. package/templates/module-monolith/tsconfig.json +20 -0
  206. package/templates/module-monolith/vite.config.monolith.mjs +8 -0
  207. package/templates/module-monolith/woodscli.json +1 -0
  208. package/templates/shared/.env.dev.example +5 -0
  209. package/templates/shared/.env.prod.example +5 -0
  210. package/templates/shared/.env.stg.example +3 -0
  211. package/templates/shared/portal.cdn.json.example +8 -0
  212. package/templates/theme-starter/package.json +5 -2
  213. package/vite/vite.config.dev.mjs +9 -1
  214. package/vite/vite.config.monolith.mjs +22 -17
  215. package/dist/chunk-JDGIAYHX.js.map +0 -1
  216. package/dist/exec-K5C6LZP4.js +0 -10
  217. /package/dist/{exec-K5C6LZP4.js.map → exec-U2OJATR3.js.map} +0 -0
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export function SectionCard({
4
+ title,
5
+ children,
6
+ className = '',
7
+ }: {
8
+ title: string
9
+ children: ReactNode
10
+ className?: string
11
+ }) {
12
+ return (
13
+ <section
14
+ className={`rounded-lg border border-[#cbd6e2] bg-white p-6 shadow-sm ${className}`}
15
+ >
16
+ <h2 className="text-base font-semibold text-[#33475b]">{title}</h2>
17
+ <div className="mt-4">{children}</div>
18
+ </section>
19
+ )
20
+ }
@@ -0,0 +1,28 @@
1
+ import { Link } from '@tanstack/react-router'
2
+
3
+ import { ROUTES } from '@/config/routes'
4
+
5
+ export function StarterNav() {
6
+ return (
7
+ <header className="border-b border-[#cbd6e2] bg-white/90 backdrop-blur">
8
+ <div className="mx-auto flex max-w-4xl items-center justify-between gap-4 px-6 py-3 sm:px-10">
9
+ <span className="text-sm font-semibold text-[#0091ae]">WoodsCLI starter</span>
10
+ <nav className="flex gap-4 text-sm font-medium">
11
+ <Link
12
+ to={ROUTES.home}
13
+ className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
14
+ activeOptions={{ exact: true }}
15
+ >
16
+ Home
17
+ </Link>
18
+ <Link
19
+ to={ROUTES.guide}
20
+ className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
21
+ >
22
+ Developer guide
23
+ </Link>
24
+ </nav>
25
+ </div>
26
+ </header>
27
+ )
28
+ }
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { StarterNav } from './starter-nav'
4
+
5
+ export function StarterShell({
6
+ children,
7
+ showNav = true,
8
+ }: {
9
+ children: ReactNode
10
+ showNav?: boolean
11
+ }) {
12
+ return (
13
+ <div className="min-h-screen bg-[#eaf0f6] text-[#33475b]">
14
+ {showNav ? <StarterNav /> : null}
15
+ {children}
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,4 @@
1
+ /** Router history — set by `wp init`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
2
+ export type RouterHistoryMode = 'hash' | 'browser'
3
+
4
+ export const ROUTER_HISTORY: RouterHistoryMode = 'hash'
@@ -0,0 +1,5 @@
1
+ /** Route paths — keep in sync with `src/routes/` file routes. */
2
+ export const ROUTES = {
3
+ home: '/',
4
+ guide: '/guide',
5
+ } as const
@@ -1,7 +1,2 @@
1
- /** Local dev fixtures for module-config runtime (greenfield wp init). */
2
- export const defaultData = {
3
- dataSourceSet: [],
4
- hubId: 'dev',
5
- userData: {},
6
- objectList: [] as unknown[],
7
- }
1
+ /** Kit runtime imports `@/defaultData` — re-export from the hubspot slice. */
2
+ export { devDefaultData as defaultData } from './hubspot/default-data'
@@ -0,0 +1,93 @@
1
+ import { SectionCard } from '@/components/layout/section-card'
2
+
3
+ import {
4
+ DEV_WORKFLOW,
5
+ PROJECT_LAYOUT,
6
+ WOODSCLI_ABOUT,
7
+ WOODSCLI_COMMANDS,
8
+ } from '../content/woodscli-guide-content'
9
+
10
+ export function StarterGuidePage() {
11
+ return (
12
+ <main className="mx-auto max-w-4xl space-y-8 px-6 py-10 sm:px-10">
13
+ <div>
14
+ <h1 className="text-2xl font-semibold tracking-tight">Developer guide</h1>
15
+ <p className="mt-2 text-sm leading-relaxed text-[#516f90]">
16
+ Commands, project layout, and workflow for this WoodsCLI module. Routes live in{' '}
17
+ <code className="font-mono text-xs">src/routes/</code> and delegate to{' '}
18
+ <code className="font-mono text-xs">src/features/</code>.
19
+ </p>
20
+ </div>
21
+
22
+ <SectionCard title="About WoodsCLI">
23
+ <p className="text-sm leading-relaxed text-[#516f90]">{WOODSCLI_ABOUT.tagline}</p>
24
+ <p className="mt-3 text-sm leading-relaxed text-[#516f90]">{WOODSCLI_ABOUT.docsHint}</p>
25
+ </SectionCard>
26
+
27
+ <SectionCard title="Essential commands">
28
+ <ul className="divide-y divide-[#eaf0f6]">
29
+ {WOODSCLI_COMMANDS.map(({ cmd, desc }) => (
30
+ <li key={cmd} className="flex flex-col gap-1 py-3 first:pt-0 last:pb-0 sm:flex-row sm:gap-4">
31
+ <code className="shrink-0 rounded bg-[#eaf0f6] px-2 py-1 font-mono text-xs text-[#0091ae]">
32
+ {cmd}
33
+ </code>
34
+ <span className="text-sm leading-relaxed text-[#516f90]">{desc}</span>
35
+ </li>
36
+ ))}
37
+ </ul>
38
+ </SectionCard>
39
+
40
+ <SectionCard title="Project layout">
41
+ <ul className="space-y-3">
42
+ {PROJECT_LAYOUT.map(({ path, note }) => (
43
+ <li key={path} className="text-sm leading-relaxed">
44
+ <code className="rounded bg-[#eaf0f6] px-1.5 py-0.5 font-mono text-xs text-[#33475b]">
45
+ {path}
46
+ </code>
47
+ <span className="mt-1 block text-[#516f90] sm:mt-0 sm:inline sm:before:content-['—_']">
48
+ {note}
49
+ </span>
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ </SectionCard>
54
+
55
+ <SectionCard title="Developer workflow">
56
+ <ol className="space-y-4">
57
+ {DEV_WORKFLOW.map(({ title: stepTitle, body }) => (
58
+ <li key={stepTitle}>
59
+ <h3 className="text-sm font-medium text-[#33475b]">{stepTitle}</h3>
60
+ <p className="mt-1 text-sm leading-relaxed text-[#516f90]">{body}</p>
61
+ </li>
62
+ ))}
63
+ </ol>
64
+ </SectionCard>
65
+
66
+ <SectionCard title="Routing">
67
+ <p className="text-sm leading-relaxed text-[#516f90]">
68
+ This starter uses the same TanStack Router setup as{' '}
69
+ <code className="font-mono text-xs">woodsportal-client-frontend</code>: file routes under{' '}
70
+ <code className="font-mono text-xs">src/routes/</code>,{' '}
71
+ <code className="font-mono text-xs">src/app.tsx</code> with{' '}
72
+ <code className="font-mono text-xs">RouterProvider</code>, and{' '}
73
+ <strong>hash history</strong> by default (
74
+ <code className="font-mono text-xs">src/config/router.ts</code>) so routes work inside
75
+ HubSpot CMS embeds. Run <code className="font-mono text-xs">wp init</code> again on a new
76
+ folder to choose browser history or no router. Add{' '}
77
+ <code className="font-mono text-xs">src/routes/about.tsx</code> and restart{' '}
78
+ <code className="font-mono text-xs">wp dev</code> to regenerate{' '}
79
+ <code className="font-mono text-xs">routeTree.gen.ts</code>.
80
+ </p>
81
+ </SectionCard>
82
+
83
+ <SectionCard title="Customize this preview">
84
+ <p className="text-sm leading-relaxed text-[#516f90]">
85
+ Edit <code className="font-mono text-xs">public/fields.json</code> to add module fields,
86
+ then adjust <code className="font-mono text-xs">src/features/starter/</code> to bind them.
87
+ Replace the starter feature with your own under{' '}
88
+ <code className="font-mono text-xs">src/features/</code> when you outgrow the demo.
89
+ </p>
90
+ </SectionCard>
91
+ </main>
92
+ )
93
+ }
@@ -0,0 +1,21 @@
1
+ import { Link } from '@tanstack/react-router'
2
+
3
+ import { ROUTES } from '@/config/routes'
4
+
5
+ import { StarterHome } from './starter-home'
6
+
7
+ export function StarterHomePage() {
8
+ return (
9
+ <>
10
+ <StarterHome />
11
+ <div className="mx-auto max-w-4xl px-6 pb-10 sm:px-10">
12
+ <Link
13
+ to={ROUTES.guide}
14
+ className="inline-flex items-center rounded-md bg-[#0091ae] px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-[#007a8c]"
15
+ >
16
+ Open developer guide →
17
+ </Link>
18
+ </div>
19
+ </>
20
+ )
21
+ }
@@ -0,0 +1,52 @@
1
+ import { useStarterPreview } from '../hooks/use-starter-preview'
2
+ import { STARTER_FEATURES, WOODSCLI_ABOUT } from '../content/woodscli-guide-content'
3
+
4
+ function heroClass(style: ReturnType<typeof useStarterPreview>['heroStyle']): string {
5
+ if (style === 'minimal') {
6
+ return 'bg-[var(--mc-bg-shell,#f5f8fa)] text-[var(--mc-text,#33475b)] border-b border-[var(--mc-border,#cbd6e2)]'
7
+ }
8
+ if (style === 'bold') {
9
+ return 'bg-[#1c1c1f] text-white'
10
+ }
11
+ return 'bg-gradient-to-br from-[var(--starter-accent,#0091ae)] to-[#ff7a59] text-white'
12
+ }
13
+
14
+ export function StarterHome() {
15
+ const { title, tagline, showFeatureCards, accentColor, heroStyle } = useStarterPreview()
16
+
17
+ return (
18
+ <>
19
+ <div style={{ ['--starter-accent' as string]: accentColor }}>
20
+ <section className={`px-6 py-14 sm:px-10 sm:py-16 ${heroClass(heroStyle)}`}>
21
+ <div className="mx-auto max-w-4xl">
22
+ <p className="mb-2 text-xs font-semibold uppercase tracking-widest opacity-80">
23
+ {WOODSCLI_ABOUT.package} · local dev
24
+ </p>
25
+ <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">{title}</h1>
26
+ <p className="mt-4 max-w-2xl text-base leading-relaxed opacity-90 sm:text-lg">{tagline}</p>
27
+ <p className="mt-6 text-sm opacity-75">
28
+ Module config overlay:{' '}
29
+ <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">Ctrl+Shift+Z</kbd>
30
+ {' · '}
31
+ Mac: <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">⌃⇧Z</kbd>
32
+ </p>
33
+ </div>
34
+ </section>
35
+ </div>
36
+
37
+ {showFeatureCards ? (
38
+ <div className="mx-auto grid max-w-4xl gap-4 px-6 py-10 sm:grid-cols-3 sm:px-10">
39
+ {STARTER_FEATURES.map((feature) => (
40
+ <article
41
+ key={feature.title}
42
+ className="rounded-lg border border-[#cbd6e2] bg-white p-5 shadow-sm"
43
+ >
44
+ <h2 className="text-sm font-semibold text-[#0091ae]">{feature.title}</h2>
45
+ <p className="mt-2 text-sm leading-relaxed text-[#516f90]">{feature.body}</p>
46
+ </article>
47
+ ))}
48
+ </div>
49
+ ) : null}
50
+ </>
51
+ )
52
+ }
@@ -0,0 +1,17 @@
1
+ import { StarterShell } from '@/components/layout/starter-shell'
2
+
3
+ import { StarterGuidePage } from './starter-guide-page'
4
+ import { StarterHome } from './starter-home'
5
+
6
+ /** Single-page starter when `wp init --router none` (no TanStack Router). */
7
+ export function StarterSinglePage() {
8
+ return (
9
+ <StarterShell showNav={false}>
10
+ <StarterHome />
11
+ <div className="mx-auto max-w-4xl px-6 sm:px-10">
12
+ <hr className="my-10 border-[#cbd6e2]" />
13
+ </div>
14
+ <StarterGuidePage />
15
+ </StarterShell>
16
+ )
17
+ }
@@ -0,0 +1,86 @@
1
+ /** Static WoodsCLI developer reference — shown on the wp init starter pages. */
2
+
3
+ export const WOODSCLI_COMMANDS = [
4
+ {
5
+ cmd: 'wp dev',
6
+ desc: 'Local Vite preview + module-config overlay (this page). Alias: wp local.',
7
+ },
8
+ {
9
+ cmd: 'wp doctor',
10
+ desc: 'Validate Node, deps, .wphs/, index.html, fields.json, and HubSpot CLI setup.',
11
+ },
12
+ {
13
+ cmd: 'wp build --tier prod',
14
+ desc: 'Production build (CDN bundle and/or HubSpot module, per projectType).',
15
+ },
16
+ {
17
+ cmd: 'wp deploy --tier prod',
18
+ desc: 'Build, publish CDN artifacts, and upload the module to HubSpot.',
19
+ },
20
+ {
21
+ cmd: 'wp adopt',
22
+ desc: 'Add woodscli.json + .wphs/ to an existing module or theme repo.',
23
+ },
24
+ {
25
+ cmd: 'wp migrate --yes',
26
+ desc: 'Move legacy .dev/ or woodsportal.hs.json to .wphs/ + woodscli.json.',
27
+ },
28
+ ] as const
29
+
30
+ export const PROJECT_LAYOUT = [
31
+ { path: 'woodscli.json', note: 'Project config — module paths, entries, dev.adapter' },
32
+ { path: 'public/fields.json', note: 'HubSpot module field schema (Content / Styles tabs)' },
33
+ { path: '.wphs/', note: 'Local workspace — config, fixtures, kit symlinks (gitignore most)' },
34
+ { path: 'wphs.adapter.ts', note: 'Syncs module field values → window.__WPHS_DATA__ in dev' },
35
+ { path: 'src/config/router.ts', note: 'Hash vs browser history — set by wp init (hash = client-frontend)' },
36
+ { path: 'src/routes/', note: 'TanStack Router file routes (thin — delegate to features/)' },
37
+ { path: 'src/app.tsx', note: 'RouterProvider shell — same role as woodsportal-client-frontend App' },
38
+ { path: 'src/features/starter/', note: 'Starter demo UI — replace with your portal features' },
39
+ { path: 'src/portal/', note: 'Dev vs production mount (mount-dev-preview, mount-portal)' },
40
+ { path: 'src/integrations/', note: 'Third-party wiring (TanStack Router, SDK, etc.)' },
41
+ { path: 'src/hubspot/', note: 'HubSpot runtime types and dev fixtures' },
42
+ { path: 'src/main.tsx', note: 'Dev entry — mounts overlay + routed preview' },
43
+ { path: 'hubspot.config.yml', note: 'HubSpot CLI portal + PAT (copy from .example)' },
44
+ ] as const
45
+
46
+ export const DEV_WORKFLOW = [
47
+ {
48
+ title: '1 · Edit module fields',
49
+ body: 'Press Ctrl+Shift+Z (Mac ⌃⇧Z) to open the HubSpot-style Content / Styles panel. Changes apply to the preview after you click Apply changes.',
50
+ },
51
+ {
52
+ title: '2 · Iterate in src/',
53
+ body: 'Add features under src/features/, shared UI under src/components/, and wire HubSpot data via wphs.adapter.ts and public/fields.json.',
54
+ },
55
+ {
56
+ title: '3 · Connect HubSpot',
57
+ body: 'Copy hubspot.config.yml.example → hubspot.config.yml, add a personal access key, then wp upload or wp watch for CMS sync.',
58
+ },
59
+ {
60
+ title: '4 · Ship',
61
+ body: 'Hybrid CDN: wp deploy. Monolith: wp build --hubspot-only then wp upload. Advanced apps: wp adopt on woodsportal-client-frontend.',
62
+ },
63
+ ] as const
64
+
65
+ export const WOODSCLI_ABOUT = {
66
+ package: '@woodsportal/hubspot-kit',
67
+ tagline:
68
+ 'WoodsCLI is WoodsPortal’s dev kit for HubSpot CMS modules and themes — Vite local preview, fields.json editing, CDN + module build pipelines, and HubSpot CLI integration.',
69
+ docsHint:
70
+ 'Full command list and guides ship in node_modules/@woodsportal/hubspot-kit/docs/ (CLI-REFERENCE.md, MODULES.md, HUBSPOT-CLI-SETUP.md).',
71
+ } as const
72
+
73
+ export const STARTER_FEATURES = [
74
+ {
75
+ title: 'Module fields',
76
+ body: 'Content and Styles tabs mirror HubSpot’s module editor. Apply changes to refresh this preview.',
77
+ },
78
+ {
79
+ title: 'Local persistence',
80
+ body: 'Saved values land in .wphs/config/module-config.local.json — safe to gitignore.',
81
+ },
82
+ {
83
+ title: 'Ship when ready',
84
+ body: 'Run wp build and wp deploy, or wp adopt to merge into the full WoodsPortal client app.',
85
+ },
86
+ ] as const
@@ -0,0 +1,42 @@
1
+ export type StarterPreviewValues = {
2
+ title: string
3
+ tagline: string
4
+ showFeatureCards: boolean
5
+ accentColor: string
6
+ heroStyle: 'gradient' | 'minimal' | 'bold'
7
+ }
8
+
9
+ function normalizeHeroStyle(raw: unknown): StarterPreviewValues['heroStyle'] {
10
+ const key = String(raw ?? 'gradient').toLowerCase()
11
+ if (key === 'minimal' || key === 'bold') return key
12
+ return 'gradient'
13
+ }
14
+
15
+ export function mapStarterPreviewValues(module: Record<string, unknown>): StarterPreviewValues {
16
+ const accent = module.accent_color as { color?: string } | undefined
17
+
18
+ return {
19
+ title: String(module.title ?? 'My HubSpot Portal'),
20
+ tagline: String(
21
+ module.tagline ??
22
+ 'Customize this preview with Ctrl+Shift+Z, then replace this page with your portal app.',
23
+ ),
24
+ showFeatureCards: module.show_feature_cards !== false,
25
+ accentColor: accent?.color ?? '#0091ae',
26
+ heroStyle: normalizeHeroStyle(module.hero_style),
27
+ }
28
+ }
29
+
30
+ /** HubSpot module field blob on window (dev adapter + CMS runtime). */
31
+ export function readStarterModuleFields(): Record<string, unknown> {
32
+ const win = window as Window & {
33
+ __WPHS_DATA__?: Record<string, unknown>
34
+ hubSpotData?: Record<string, unknown>
35
+ }
36
+ const blob = win.__WPHS_DATA__ ?? win.hubSpotData ?? {}
37
+ const nested = blob.module
38
+ if (nested && typeof nested === 'object') {
39
+ return nested as Record<string, unknown>
40
+ }
41
+ return blob
42
+ }
@@ -0,0 +1,17 @@
1
+ import { MODULE_CONFIG_CHANGED } from '@/dev/module-config/schema/types'
2
+ import { getModuleFieldValues } from '@/dev/module-config/runtime/module-config-runtime'
3
+
4
+ import {
5
+ mapStarterPreviewValues,
6
+ type StarterPreviewValues,
7
+ } from './starter-preview-values'
8
+
9
+ /** Dev-only: refresh when module-config overlay applies changes. */
10
+ export function subscribeStarterPreview(
11
+ onChange: (values: StarterPreviewValues) => void,
12
+ ): () => void {
13
+ const refresh = () => onChange(mapStarterPreviewValues(getModuleFieldValues()))
14
+
15
+ window.addEventListener(MODULE_CONFIG_CHANGED, refresh)
16
+ return () => window.removeEventListener(MODULE_CONFIG_CHANGED, refresh)
17
+ }
@@ -0,0 +1,30 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ import {
4
+ mapStarterPreviewValues,
5
+ readStarterModuleFields,
6
+ type StarterPreviewValues,
7
+ } from './starter-preview-values'
8
+
9
+ /** Module field values for the starter preview (dev overlay + production window data). */
10
+ export function useStarterPreview(): StarterPreviewValues {
11
+ const [values, setValues] = useState(() => mapStarterPreviewValues(readStarterModuleFields()))
12
+
13
+ useEffect(() => {
14
+ if (!import.meta.env.DEV) {
15
+ return
16
+ }
17
+
18
+ let unsubscribe = () => {}
19
+
20
+ void import('./use-starter-preview.dev').then(({ subscribeStarterPreview }) => {
21
+ unsubscribe = subscribeStarterPreview(setValues)
22
+ })
23
+
24
+ return () => unsubscribe()
25
+ }, [])
26
+
27
+ return values
28
+ }
29
+
30
+ export type { StarterPreviewValues } from './starter-preview-values'
@@ -0,0 +1,6 @@
1
+ export { StarterHomePage } from './components/starter-home-page'
2
+ export { StarterGuidePage } from './components/starter-guide-page'
3
+ export { StarterSinglePage } from './components/starter-single-page'
4
+ export { StarterHome } from './components/starter-home'
5
+ export { useStarterPreview } from './hooks/use-starter-preview'
6
+ export type { StarterPreviewValues } from './hooks/starter-preview-values'
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Thin HubSpot module.js bootstrap — JS app loads from CDN via require_js in module.html.
3
+ */
4
+ declare global {
5
+ interface Window {
6
+ WoodsPortalHubSpotBootstrap?: { ready: true }
7
+ }
8
+ }
9
+
10
+ window.WoodsPortalHubSpotBootstrap = { ready: true }
@@ -0,0 +1,7 @@
1
+ /** Local dev fixtures merged by the module-config runtime in `wp dev`. */
2
+ export const devDefaultData = {
3
+ dataSourceSet: [],
4
+ hubId: 'dev',
5
+ userData: {},
6
+ objectList: [] as unknown[],
7
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Monolith production entry — single IIFE `module.js` (wp build).
3
+ * Dev overlay uses `src/main.tsx` instead.
4
+ */
5
+ import '@/assets/css/main.dev.css'
6
+
7
+ import { mountPortal } from '@/portal/mount-portal'
8
+
9
+ void mountPortal()
@@ -0,0 +1,25 @@
1
+ import {
2
+ createBrowserHistory,
3
+ createHashHistory,
4
+ createRouter,
5
+ } from '@tanstack/react-router'
6
+
7
+ import { ROUTER_HISTORY } from '@/config/router'
8
+ import { routeTree } from '@/routeTree.gen'
9
+
10
+ const history = ROUTER_HISTORY === 'hash' ? createHashHistory() : createBrowserHistory()
11
+
12
+ export const router = createRouter({
13
+ routeTree,
14
+ history,
15
+ defaultPreload: 'intent',
16
+ scrollRestoration: true,
17
+ defaultStructuralSharing: true,
18
+ defaultPreloadStaleTime: 0,
19
+ })
20
+
21
+ declare module '@tanstack/react-router' {
22
+ interface Register {
23
+ router: typeof router
24
+ }
25
+ }
@@ -1,4 +1,5 @@
1
- import './assets/css/main.dev.css'
2
- import { mountGreenfieldDev } from '@woodsportal/hubspot-kit/dev/bootstrap-greenfield'
1
+ import '@/assets/css/main.dev.css'
3
2
 
4
- void mountGreenfieldDev()
3
+ import { mountDevPreview } from '@/portal/mount-dev-preview'
4
+
5
+ void mountDevPreview()
@@ -0,0 +1,8 @@
1
+ import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
2
+
3
+ import { App } from '@/app'
4
+
5
+ /** Local dev: module-config overlay + routed starter preview. */
6
+ export async function mountDevPreview() {
7
+ await mountGreenfieldDev({ preview: <App /> })
8
+ }
@@ -0,0 +1,12 @@
1
+ import ReactDOM from 'react-dom/client'
2
+
3
+ import { App } from '@/app'
4
+
5
+ /** Production mount — same App shell as woodsportal-client-frontend CDN app chunk. */
6
+ export async function mountPortal() {
7
+ const root = document.getElementById('app')
8
+ if (!root) {
9
+ return
10
+ }
11
+ ReactDOM.createRoot(root).render(<App />)
12
+ }
@@ -0,0 +1,77 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as GuideRouteImport } from './routes/guide'
13
+ import { Route as IndexRouteImport } from './routes/index'
14
+
15
+ const GuideRoute = GuideRouteImport.update({
16
+ id: '/guide',
17
+ path: '/guide',
18
+ getParentRoute: () => rootRouteImport,
19
+ } as any)
20
+ const IndexRoute = IndexRouteImport.update({
21
+ id: '/',
22
+ path: '/',
23
+ getParentRoute: () => rootRouteImport,
24
+ } as any)
25
+
26
+ export interface FileRoutesByFullPath {
27
+ '/': typeof IndexRoute
28
+ '/guide': typeof GuideRoute
29
+ }
30
+ export interface FileRoutesByTo {
31
+ '/': typeof IndexRoute
32
+ '/guide': typeof GuideRoute
33
+ }
34
+ export interface FileRoutesById {
35
+ __root__: typeof rootRouteImport
36
+ '/': typeof IndexRoute
37
+ '/guide': typeof GuideRoute
38
+ }
39
+ export interface FileRouteTypes {
40
+ fileRoutesByFullPath: FileRoutesByFullPath
41
+ fullPaths: '/' | '/guide'
42
+ fileRoutesByTo: FileRoutesByTo
43
+ to: '/' | '/guide'
44
+ id: '__root__' | '/' | '/guide'
45
+ fileRoutesById: FileRoutesById
46
+ }
47
+ export interface RootRouteChildren {
48
+ IndexRoute: typeof IndexRoute
49
+ GuideRoute: typeof GuideRoute
50
+ }
51
+
52
+ declare module '@tanstack/react-router' {
53
+ interface FileRoutesByPath {
54
+ '/guide': {
55
+ id: '/guide'
56
+ path: '/guide'
57
+ fullPath: '/guide'
58
+ preLoaderRoute: typeof GuideRouteImport
59
+ parentRoute: typeof rootRouteImport
60
+ }
61
+ '/': {
62
+ id: '/'
63
+ path: '/'
64
+ fullPath: '/'
65
+ preLoaderRoute: typeof IndexRouteImport
66
+ parentRoute: typeof rootRouteImport
67
+ }
68
+ }
69
+ }
70
+
71
+ const rootRouteChildren: RootRouteChildren = {
72
+ IndexRoute: IndexRoute,
73
+ GuideRoute: GuideRoute,
74
+ }
75
+ export const routeTree = rootRouteImport
76
+ ._addFileChildren(rootRouteChildren)
77
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,15 @@
1
+ import { Outlet, createRootRoute } from '@tanstack/react-router'
2
+
3
+ import { StarterShell } from '@/components/layout/starter-shell'
4
+
5
+ function RootLayout() {
6
+ return (
7
+ <StarterShell>
8
+ <Outlet />
9
+ </StarterShell>
10
+ )
11
+ }
12
+
13
+ export const Route = createRootRoute({
14
+ component: RootLayout,
15
+ })
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { StarterGuidePage } from '@/features/starter'
4
+
5
+ export const Route = createFileRoute('/guide')({
6
+ component: StarterGuidePage,
7
+ })