From 74107ce6b15806e557a2362c4d730a864036b383 Mon Sep 17 00:00:00 2001
From: kccarlos <110118511+kccarlos@users.noreply.github.com>
Date: Tue, 6 Oct 2026 16:29:07 -0700
Subject: [PATCH 1/5] fix(web): serve from kccarlos.github.io/gitcontext and
stop token-count restart loop
The custom domain gitcontext.xyz is gone, so production builds use the
/gitcontext/ base and the CNAME is removed. A Playwright smoke test checks
the built site under that path.
TokenCountsProvider passed a new onBatch on every render; it is a
dependency of the counting effect, so each progress update restarted
counting and flooded the git worker (production builds never finished
counting).
Claude-Session: https://claude.ai/code/session_01Yb4sbCDrvzqV8fFpnCrQK5
---
apps/web/package.json | 7 ++-
apps/web/playwright.pages.config.ts | 20 ++++++++
apps/web/public/CNAME | 1 -
.../src/context/TokenCountsContext.test.tsx | 50 +++++++++++++++++++
apps/web/src/context/TokenCountsContext.tsx | 20 +++++---
apps/web/tests/pages/pages.spec.ts | 22 ++++++++
apps/web/vite.config.ts | 8 +--
package-lock.json | 8 +--
8 files changed, 119 insertions(+), 17 deletions(-)
create mode 100644 apps/web/playwright.pages.config.ts
delete mode 100644 apps/web/public/CNAME
create mode 100644 apps/web/src/context/TokenCountsContext.test.tsx
create mode 100644 apps/web/tests/pages/pages.spec.ts
diff --git a/apps/web/package.json b/apps/web/package.json
index 7fbc5c7..5f09305 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -6,7 +6,7 @@
"type": "git",
"url": "https://github.com/kccarlos/gitcontext.git"
},
- "homepage": "https://gitcontext.xyz/",
+ "homepage": "https://kccarlos.github.io/gitcontext/",
"bugs": {
"url": "https://github.com/kccarlos/gitcontext/issues"
},
@@ -31,7 +31,8 @@
"preview": "vite preview",
"test:e2e": "playwright test",
"test:unit": "vitest run --reporter=dot",
- "test": "npm run test:unit && npm run test:e2e"
+ "test": "npm run test:unit && npm run test:e2e",
+ "test:pages": "playwright test -c playwright.pages.config.ts"
},
"dependencies": {
"@dqbd/tiktoken": "^1.0.22",
@@ -54,6 +55,7 @@
"devDependencies": {
"@eslint/js": "^9.32.0",
"@playwright/test": "^1.48.2",
+ "@testing-library/react": "16.3.3",
"@types/node": "^22.7.4",
"@types/prismjs": "^1.26.5",
"@types/react": ">=18 <19",
@@ -63,6 +65,7 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
+ "jsdom": "25.0.1",
"typescript": "~5.8.3",
"typescript-eslint": "^8.39.0",
"vite": "^7.1.0",
diff --git a/apps/web/playwright.pages.config.ts b/apps/web/playwright.pages.config.ts
new file mode 100644
index 0000000..eefc424
--- /dev/null
+++ b/apps/web/playwright.pages.config.ts
@@ -0,0 +1,20 @@
+import { defineConfig, devices } from '@playwright/test'
+
+// Smoke test of the production build as GitHub Pages serves it, under
+// /gitcontext/. Run after `vite build`: npm run test:pages
+export default defineConfig({
+ testDir: './tests/pages',
+ forbidOnly: !!process.env.CI,
+ reporter: [['list']],
+ use: {
+ baseURL: 'http://localhost:4173/gitcontext/',
+ headless: true,
+ },
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
+ webServer: {
+ command: 'npx vite preview --port 4173 --strictPort',
+ url: 'http://localhost:4173/gitcontext/',
+ reuseExistingServer: false,
+ timeout: 60_000,
+ },
+})
diff --git a/apps/web/public/CNAME b/apps/web/public/CNAME
deleted file mode 100644
index 46454d9..0000000
--- a/apps/web/public/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-gitcontext.xyz
diff --git a/apps/web/src/context/TokenCountsContext.test.tsx b/apps/web/src/context/TokenCountsContext.test.tsx
new file mode 100644
index 0000000..ee5d74f
--- /dev/null
+++ b/apps/web/src/context/TokenCountsContext.test.tsx
@@ -0,0 +1,50 @@
+// @vitest-environment jsdom
+import { describe, it, expect, vi } from 'vitest'
+import { render, waitFor } from '@testing-library/react'
+import type { GitEngine } from '../platform/types'
+import type { FileDiffStatus } from '../hooks/useFileTree'
+
+vi.mock('../platform/tokenizerFactory', () => ({
+ createTokenizer: () => ({
+ async count(text: string) { return text.length },
+ async warmup() {},
+ }),
+}))
+
+import { TokenCountsProvider, useTokenCountsContext } from './TokenCountsContext'
+
+function Total({ onRender }: { onRender: (total: number, busy: boolean) => void }) {
+ const { total, busy } = useTokenCountsContext()
+ onRender(total, busy)
+ return null
+}
+
+describe('TokenCountsProvider', () => {
+ it('counts each selected file once instead of restarting on its own progress updates', async () => {
+ const readFile = vi.fn(async (_ref: string, path: string) => ({ text: `content of ${path}\n` }))
+ const gitClient = { readFile } as unknown as GitEngine
+ const selectedPaths = new Set(['a.ts', 'b.ts'])
+ const statusByPath = new Map
+ Try it in your browser · + Install · + How it works +
+ +
+
+
![]() Diff preview: unified or side-by-side, before you include a file. |
+ ![]() Dark mode, following the system or set by hand. |
+
![]() Selected files with per-file token counts, sorted by size, then one click to copy the whole prompt. |
+ |
&^N%h)U|dTv6FVU9hSA
za%1A^&%hJt%Tj8ZF#zO&`{of zQEBJUTWMpL~)Z8v_}IE_{4niNhvr3nIj__mu2jkmv2UM7A!xd40I@$
z1Nn%a@wWgYFtIT;Pg0;ym*?1yCN_0o^pXK71#kA>pai$iZiLS
z$}Kd(P&;jorOH-6p`vQ90jVtgqF4U4=9ZS8VAUWtM%8oGWT&I}_Yh3WB9cbIppZ(v
zgqn!h_(IiwuS4iG^oz}zfvI%8&yTuBp<_o;HH-eCrbQ6go6;d`9(VmSKf)Qmow!tv
zIV5)88jBwbYzUT?H*-5+Qf$C3{j&3B{gKrBkP>BN9n?PmL*NHkx$@Sr+Zh*=C%t;t
ze(JL9Lr5h&+S^B=8{ncl2X!M|fiIpe^o7DeL(0%d$A=i*!lM0^O2Rcglbs{oVAq%N1swOUtQcb$$A-F6DKC(}jlbAQ~1M8=GN_
zxAfMQD-~yHRVQj$GiO_wj)y~lLx6&d8KB`j>>QmOoSC}xEB;J{J`8%B)h<}utLOd{
zs+5)ZJ2PvGmEqRR7|+$>*`IgH%-r|NTNw{;)zem~Jb2Gh)`5~L#21xaPimu%A54Oc
zbeShgtpO?FaFR0vV3qVKzIruaykGBayeSFvo#9mTI9
b
zwPIWF*zzn%Fd(enW34dpOUrn++%O!ly?ne7V~g#dl5pZ$8Ok2`_99VGx~0QNn%e$5
z_vv_6m=s=kT>)uau6uD%yePIR@5_JpfhN4`JZaBRB(FdAp)`20u+cP*oNzw&+isfu
zILp9o)oH#m-Pq{vT%eu)dAp`Jxt`B<`_%_&=F7S&-AVUF(+9
zthKv$g0Js+O9HZ6qdoj|im5+_`lFqS7f!ke5&ip$Cr
pH(s
zGO-g2!n&H5a}S<9{v!WUW+|qT+C1o_i#B&Abwsc0hGKN>=tgbL<}Dx$