create-grist-widget 0.2.12 → 0.2.13

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/README.md CHANGED
@@ -46,6 +46,11 @@ simplest way to get from zero to a deployed widget:
46
46
  folder `/ (root)`. This can only be set once `gh-pages` exists, which is
47
47
  why it's a step *after* the first push, not before.
48
48
 
49
+ > ⚠️ **Not `main`.** Pointing Pages at `main` serves this repo's raw,
50
+ > unbuilt source instead of the built site — the symptom is a blank page
51
+ > with a `/src/main.tsx` 404 in the browser console, even though the
52
+ > workflow itself reports success.
53
+
49
54
  Once that's saved, your widget is live at two URLs:
50
55
 
51
56
  - `https://<owner>.github.io/<repo>/latest/` — mutable, always points at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-grist-widget",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "Scaffold a new Grist custom widget from the grist-widget-sdk Vite template",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,8 +13,11 @@ name: Deploy widget to GitHub Pages
13
13
  # One-time setup this workflow can't do for you: enable Pages (Settings ->
14
14
  # Pages -> Source: Deploy from a branch -> gh-pages -> / (root); this workflow
15
15
  # creates the gh-pages branch itself if missing, but Pages must be pointed at
16
- # it once) and make sure Actions has write access (Settings -> Actions ->
17
- # General -> Workflow permissions -> Read and write permissions).
16
+ # it once -- NOT main, which serves this repo's raw unbuilt source instead
17
+ # (symptom: a blank page with a 404 for /src/main.tsx in the browser
18
+ # console, even though this workflow reports success) and make sure Actions
19
+ # has write access (Settings -> Actions -> General -> Workflow permissions ->
20
+ # Read and write permissions).
18
21
 
19
22
  on:
20
23
  push:
@@ -56,10 +59,16 @@ jobs:
56
59
  uses: actions/setup-node@v4
57
60
  with:
58
61
  node-version: 22
59
- cache: pnpm
60
62
 
63
+ # Not --frozen-lockfile, and no `cache: pnpm` above: a freshly
64
+ # scaffolded repo never has a committed pnpm-lock.yaml (the CLI's
65
+ # auto-commit runs before anyone's first `pnpm install`), and
66
+ # --frozen-lockfile/cache: pnpm both hard-require one to exist —
67
+ # found live, the very first real deploy failed with "Dependencies
68
+ # lock file is not found". A plain install resolves fresh every run
69
+ # regardless of whether a lockfile was ever committed.
61
70
  - name: Install dependencies
62
- run: pnpm install --frozen-lockfile
71
+ run: pnpm install
63
72
 
64
73
  - name: Clone gh-pages into ./site
65
74
  run: |
@@ -92,6 +92,12 @@ automatically.
92
92
  → `/ (root)`. The workflow creates the `gh-pages` branch itself the first
93
93
  time it runs (if it doesn't exist yet), but Pages needs to be pointed at
94
94
  it once.
95
+ > ⚠️ **Not `main`.** If Pages is left on (or accidentally set to) `main`,
96
+ > it serves this repo's raw, unbuilt source — including a script tag
97
+ > pointing at `/src/main.tsx` — instead of the built site. The symptom is
98
+ > a blank/black page with a 404 for `/src/main.tsx` in the browser
99
+ > console, even though the workflow itself reports success (it pushed the
100
+ > right build to `gh-pages`; Pages is just reading from the wrong place).
95
101
  2. **Settings → Actions → General → Workflow permissions** → "Read and write
96
102
  permissions". New repos sometimes default the workflow's token to
97
103
  read-only, which would fail the push to `gh-pages` with a 403.
@@ -133,12 +133,22 @@ export function placeTarget({ siteDir, channel, version, distDir, sha, ref }) {
133
133
  }
134
134
  return { placed: "dev" }
135
135
  }
136
- // release
136
+ // release: immutable v<version>/, mutable latest/, and the same build
137
+ // placed directly at the site root too. main.tsx renders the showcase hub
138
+ // (TemplateLanding) at any URL with no recognized channel suffix — the
139
+ // bare root included — so without this, a real scaffolded repo's own
140
+ // https://owner.github.io/repo/ has nothing deployed there at all and
141
+ // 404s instead of ever reaching that hub. Reusing the release dist here is
142
+ // exactly like latest/ reusing it: the asset URLs it references (under
143
+ // v<version>/assets/) already exist from the versionDir placement above,
144
+ // so nothing 404s.
137
145
  const versionDir = join(siteDir, `v${version}`)
138
146
  replaceDir(versionDir, distDir)
139
147
  const latestDir = join(siteDir, "latest")
140
148
  replaceDir(latestDir, distDir)
141
- return { placed: `v${version}`, latest: "latest" }
149
+ mkdirSync(siteDir, { recursive: true })
150
+ cpSync(distDir, siteDir, { recursive: true })
151
+ return { placed: `v${version}`, latest: "latest", root: true }
142
152
  }
143
153
 
144
154
  /**
@@ -49,7 +49,7 @@ function StatusDot({ pulsing, className }: { pulsing: boolean; className: string
49
49
  )
50
50
  }
51
51
 
52
- /** Pure render of a display status -- shared by the real (context-driven) and demo (cycling) chips. */
52
+ /** Pure render of a display status. */
53
53
  function ChipVisual({ display, secondsLeft }: { display: HandshakeDisplayStatus; secondsLeft: number }) {
54
54
  switch (display.kind) {
55
55
  case "connecting":
@@ -91,81 +91,27 @@ function RealChipContents() {
91
91
  return <ChipVisual display={display} secondsLeft={secondsLeft} />
92
92
  }
93
93
 
94
- // A fixed, looping sequence for the showcase hub -- there's no real Grist to
95
- // connect to on that page (it's shown precisely because a real widget isn't
96
- // embedded), so this is a cosmetic demo of the same visual states a real
97
- // widget's chip goes through, not a real handshake attempt.
98
- const DEMO_SEQUENCE: readonly { display: HandshakeDisplayStatus; ms: number }[] = [
99
- { display: { kind: "connecting" }, ms: 1500 },
100
- { display: { kind: "retrying", attempts: 1 }, ms: 3000 },
101
- { display: { kind: "connected" }, ms: 3500 },
102
- ]
103
-
104
- function DemoChipContents() {
105
- const [step, setStep] = useState(0)
106
- const [countdownState, setCountdownState] = useState({ step, secondsLeft: 3 })
107
- const current = DEMO_SEQUENCE[step]
108
-
109
- // Reset the mini countdown the moment the demo cycle enters "retrying" --
110
- // an "adjust state during render" pattern, not a setState-in-effect.
111
- if (current.display.kind === "retrying" && countdownState.step !== step) {
112
- setCountdownState({ step, secondsLeft: 3 })
113
- }
114
-
115
- useEffect(() => {
116
- const advance = setTimeout(() => {
117
- setStep((s) => (s + 1) % DEMO_SEQUENCE.length)
118
- }, current.ms)
119
- return () => clearTimeout(advance)
120
- }, [step, current.ms])
121
-
122
- useEffect(() => {
123
- if (current.display.kind !== "retrying") return
124
- const id = setInterval(() => {
125
- setCountdownState((s) => ({ ...s, secondsLeft: s.secondsLeft > 0 ? s.secondsLeft - 1 : 0 }))
126
- }, 1000)
127
- return () => clearInterval(id)
128
- }, [step, current.display.kind])
129
-
130
- return <ChipVisual display={current.display} secondsLeft={countdownState.secondsLeft} />
131
- }
132
-
133
94
  /**
134
- * Small pill showing Grist handshake status. Two modes:
135
- *
136
- * - Default: live status while actually embedded in Grist. Mounts its own
137
- * `<GristHandshakeProvider>` alongside `<GristWidgetProvider>` --
138
- * documented (grist-widget-sdk/advanced) as safe to coexist, since both
139
- * share the page's `ensureGristReady()` singleton. Purely observational:
140
- * it never drives the real widget's connection, just reflects it.
141
- * - `demo`: a cosmetic, looping cycle through the same visual states, used
142
- * on the showcase hub page where no real Grist connection is ever
143
- * attempted -- a reference to the tech behind the template, not a status
144
- * readout.
95
+ * Small pill showing live Grist handshake status while actually embedded in
96
+ * Grist. Mounts its own `<GristHandshakeProvider>` alongside
97
+ * `<GristWidgetProvider>` -- documented (grist-widget-sdk/advanced) as safe
98
+ * to coexist, since both share the page's `ensureGristReady()` singleton.
99
+ * Purely observational: it never drives the real widget's connection, just
100
+ * reflects it.
145
101
  */
146
- export function GristStatusChip({
147
- demo = false,
148
- className,
149
- }: {
150
- demo?: boolean
151
- className?: string
152
- }) {
153
- const pill = (
154
- <div
155
- className={cn(
156
- "flex items-center gap-1.5 rounded-full border bg-background/90 px-2.5 py-1 text-xs text-muted-foreground shadow-sm backdrop-blur",
157
- className,
158
- )}
159
- >
160
- {demo ? <DemoChipContents /> : <RealChipContents />}
161
- </div>
162
- )
163
-
164
- if (demo) return pill
165
-
102
+ export function GristStatusChip({ className }: { className?: string }) {
166
103
  return (
167
104
  <GristHandshakeProvider>
168
- <div className="pointer-events-none fixed top-2 right-2 z-50">{pill}</div>
105
+ <div className="pointer-events-none fixed top-2 right-2 z-50">
106
+ <div
107
+ className={cn(
108
+ "flex items-center gap-1.5 rounded-full border bg-background/90 px-2.5 py-1 text-xs text-muted-foreground shadow-sm backdrop-blur",
109
+ className,
110
+ )}
111
+ >
112
+ <RealChipContents />
113
+ </div>
114
+ </div>
169
115
  </GristHandshakeProvider>
170
116
  )
171
117
  }
@@ -1,6 +1,5 @@
1
1
  import { useState } from "react"
2
2
 
3
- import { GristStatusChip } from "@/components/grist-status-chip"
4
3
  import { Button } from "@/components/ui/button"
5
4
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
6
5
  import { DEV_URL, formatDate, useVersions, versionUrl } from "@/lib/showcase-versions"
@@ -70,9 +69,6 @@ export function TemplateLanding() {
70
69
  return (
71
70
  <div className="mx-auto max-w-2xl px-6 py-16">
72
71
  <header className="mb-10 text-center">
73
- <div className="mb-4 flex justify-center">
74
- <GristStatusChip demo />
75
- </div>
76
72
  <p className="mb-2 text-sm font-medium text-muted-foreground">
77
73
  grist-widget-template
78
74
  </p>
@@ -157,6 +153,13 @@ git push origin dev`}</code>
157
153
  enable Pages: Settings → Pages → Source → "Deploy from a
158
154
  branch" → branch <code className="text-foreground">gh-pages</code>{" "}
159
155
  → <code className="text-foreground">/ (root)</code>.
156
+ <p className="mt-2 text-xs text-muted-foreground">
157
+ Not <code className="text-foreground">main</code> — that
158
+ serves this repo's raw, unbuilt source instead of the built
159
+ site (a blank page with a{" "}
160
+ <code className="text-foreground">/src/main.tsx</code> 404
161
+ in the console).
162
+ </p>
160
163
  <p className="mt-2 text-xs text-muted-foreground">
161
164
  If that first run fails with a permissions error pushing to{" "}
162
165
  <code className="text-foreground">gh-pages</code>, Settings