From 3b9077cb0ca9d0ffa3219007051bd9069a590fb7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:16:45 +0000 Subject: [PATCH 1/4] Initial plan From 28667dc83abbe1a0ea224015149fdb2f7c6eb176 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:25:19 +0000 Subject: [PATCH 2/4] feat: display widget version in all widgets Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com> --- docs/widget-author-instructions.md | 19 +++++++++++++++ .../ai-credits-widget/src/AiCreditsWidget.tsx | 3 +++ .../ai-credits-widget/tsconfig.build.json | 4 ++-- packages/ai-credits-widget/tsconfig.json | 4 ++-- .../src/CitizenClaimWidget.tsx | 3 +++ .../citizen-claim-widget/tsconfig.build.json | 4 ++-- packages/citizen-claim-widget/tsconfig.json | 4 ++-- .../src/ClaimWidget.tsx | 3 +++ .../tsconfig.build.json | 4 ++-- .../claim-widget-theme-demo/tsconfig.json | 4 ++-- .../gooddata-widget/src/GoodDataWidget.tsx | 4 +++- packages/gooddata-widget/tsconfig.build.json | 4 ++-- packages/gooddata-widget/tsconfig.json | 4 ++-- .../src/GoodReserveWidget.tsx | 3 +++ .../goodreserve-widget/tsconfig.build.json | 4 ++-- packages/goodreserve-widget/tsconfig.json | 4 ++-- .../src/GovernanceOnboardingWidget.tsx | 4 +++- .../src/GovernanceWidget.tsx | 3 +++ .../governance-widget/tsconfig.build.json | 4 ++-- packages/governance-widget/tsconfig.json | 4 ++-- .../src/StakingMigrationWidget.tsx | 3 +++ .../tsconfig.build.json | 4 ++-- .../staking-migration-widget/tsconfig.json | 4 ++-- .../streaming-widget/src/StreamingWidget.tsx | 4 ++++ packages/streaming-widget/tsconfig.build.json | 4 ++-- packages/streaming-widget/tsconfig.json | 4 ++-- .../src/SuperfluidCampaignWidget.tsx | 3 +++ .../tsconfig.build.json | 4 ++-- .../superfluid-campaign-widget/tsconfig.json | 4 ++-- packages/ui/src/components/WidgetVersion.tsx | 24 +++++++++++++++++++ packages/ui/src/index.ts | 2 ++ 31 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 packages/ui/src/components/WidgetVersion.tsx diff --git a/docs/widget-author-instructions.md b/docs/widget-author-instructions.md index fdf109aa..78097052 100644 --- a/docs/widget-author-instructions.md +++ b/docs/widget-author-instructions.md @@ -298,6 +298,24 @@ inline. It becomes hard to review and hard to theme correctly. --- +## Widget Version Label + +Every widget must show its own version, read from the widget package's `package.json`, as a +small muted label in the bottom-right corner. Render the shared `WidgetVersion` primitive as the +last child of the widget's root (inside `GoodWidgetProvider`) so it never overlaps content: + +```tsx +import { WidgetVersion } from '@goodwidget/ui' +import packageJson from '../package.json' + + +``` + +The widget's `tsconfig.json` and `tsconfig.build.json` must use `"rootDir": "."` and include +`package.json` so the import type-checks. + +--- + ## Review Checklist Before requesting review: @@ -310,6 +328,7 @@ Before requesting review: - Light and dark states were checked when the component supports both. - Mobile width was checked against the design reference. - Storybook screenshots were refreshed when visual states changed. +- The widget renders ``. - Manual visual review was done against Figma/Stitch/reference screenshots. If a design seems impossible to express through the current system, do not work around it by diff --git a/packages/ai-credits-widget/src/AiCreditsWidget.tsx b/packages/ai-credits-widget/src/AiCreditsWidget.tsx index fb46271a..3dd68c59 100644 --- a/packages/ai-credits-widget/src/AiCreditsWidget.tsx +++ b/packages/ai-credits-widget/src/AiCreditsWidget.tsx @@ -20,6 +20,8 @@ import { createToast, updateToast, } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { needsWalletConnection, useAiCreditsAdapter } from './adapter' import { useAiCreditsHistory } from './useAiCreditsHistory' import { @@ -649,6 +651,7 @@ export function AiCreditsWidget({ showWalletControls={showWalletControls} /> + ) diff --git a/packages/ai-credits-widget/tsconfig.build.json b/packages/ai-credits-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/ai-credits-widget/tsconfig.build.json +++ b/packages/ai-credits-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/ai-credits-widget/tsconfig.json b/packages/ai-credits-widget/tsconfig.json index 006b6f40..5f693f93 100644 --- a/packages/ai-credits-widget/tsconfig.json +++ b/packages/ai-credits-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -10,5 +10,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx b/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx index ce73257e..9b347785 100644 --- a/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx +++ b/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx @@ -20,6 +20,8 @@ import { YStack, WidgetTabs, } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { SupportedChains } from '@goodsdks/citizen-sdk' import { getChainDisplayName, useCitizenClaimAdapter } from './adapter' import type { @@ -687,6 +689,7 @@ export function CitizenClaimWidget({ onClaimError={onClaimError} initialTab={initialTab} /> + ) } diff --git a/packages/citizen-claim-widget/tsconfig.build.json b/packages/citizen-claim-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/citizen-claim-widget/tsconfig.build.json +++ b/packages/citizen-claim-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/citizen-claim-widget/tsconfig.json b/packages/citizen-claim-widget/tsconfig.json index 006b6f40..5f693f93 100644 --- a/packages/citizen-claim-widget/tsconfig.json +++ b/packages/citizen-claim-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -10,5 +10,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/claim-widget-theme-demo/src/ClaimWidget.tsx b/packages/claim-widget-theme-demo/src/ClaimWidget.tsx index f6db42da..e8f47222 100644 --- a/packages/claim-widget-theme-demo/src/ClaimWidget.tsx +++ b/packages/claim-widget-theme-demo/src/ClaimWidget.tsx @@ -19,6 +19,8 @@ import { XStack, YStack, } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' const ClaimCard = createComponent(Card, { name: 'ClaimCard', @@ -261,6 +263,7 @@ export function ClaimWidget({ defaultTheme={defaultTheme} > + ) } diff --git a/packages/claim-widget-theme-demo/tsconfig.build.json b/packages/claim-widget-theme-demo/tsconfig.build.json index c2f740fe..87d73765 100644 --- a/packages/claim-widget-theme-demo/tsconfig.build.json +++ b/packages/claim-widget-theme-demo/tsconfig.build.json @@ -2,11 +2,11 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/ui": ["../ui/src/index.ts"], "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/claim-widget-theme-demo/tsconfig.json b/packages/claim-widget-theme-demo/tsconfig.json index 66f66cf7..5ab7b0d2 100644 --- a/packages/claim-widget-theme-demo/tsconfig.json +++ b/packages/claim-widget-theme-demo/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -11,5 +11,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/gooddata-widget/src/GoodDataWidget.tsx b/packages/gooddata-widget/src/GoodDataWidget.tsx index 6c686812..0da42325 100644 --- a/packages/gooddata-widget/src/GoodDataWidget.tsx +++ b/packages/gooddata-widget/src/GoodDataWidget.tsx @@ -10,7 +10,8 @@ */ import React from 'react' import { BrowserRouter, Link, Route, Routes, useParams } from 'react-router-dom' -import { Heading, Text, YStack } from '@goodwidget/ui' +import { Heading, Text, WidgetVersion, YStack } from '@goodwidget/ui' +import packageJson from '../package.json' import { getDashboard, listDashboards } from './dashboards/registry' const CONTENT_MAX_WIDTH = 1120 @@ -103,6 +104,7 @@ export function GoodDataWidget({ basename }: GoodDataWidgetProps = {}) { } /> + ) diff --git a/packages/gooddata-widget/tsconfig.build.json b/packages/gooddata-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/gooddata-widget/tsconfig.build.json +++ b/packages/gooddata-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/gooddata-widget/tsconfig.json b/packages/gooddata-widget/tsconfig.json index 1821cb56..bcceced4 100644 --- a/packages/gooddata-widget/tsconfig.json +++ b/packages/gooddata-widget/tsconfig.json @@ -2,12 +2,12 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/embed": ["../embed/src/index.ts"], "@goodwidget/ui": ["../ui/src/index.ts"], "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/goodreserve-widget/src/GoodReserveWidget.tsx b/packages/goodreserve-widget/src/GoodReserveWidget.tsx index 52368652..93f844ed 100644 --- a/packages/goodreserve-widget/src/GoodReserveWidget.tsx +++ b/packages/goodreserve-widget/src/GoodReserveWidget.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useRef } from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import { YStack } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import type { EIP1193Provider } from '@goodwidget/core' import { ReserveSwapView } from './ReserveSwapView' import { useGoodReserveAdapter } from './useGoodReserveAdapter' @@ -75,6 +77,7 @@ export function GoodReserveWidget({ mockState={mockState} preferredChainId={preferredChainId} /> + ) diff --git a/packages/goodreserve-widget/tsconfig.build.json b/packages/goodreserve-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/goodreserve-widget/tsconfig.build.json +++ b/packages/goodreserve-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/goodreserve-widget/tsconfig.json b/packages/goodreserve-widget/tsconfig.json index 66f66cf7..5ab7b0d2 100644 --- a/packages/goodreserve-widget/tsconfig.json +++ b/packages/goodreserve-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -11,5 +11,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/governance-widget/src/GovernanceOnboardingWidget.tsx b/packages/governance-widget/src/GovernanceOnboardingWidget.tsx index 4439c7a8..36fe0989 100644 --- a/packages/governance-widget/src/GovernanceOnboardingWidget.tsx +++ b/packages/governance-widget/src/GovernanceOnboardingWidget.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react' -import { PageWizardProvider } from '@goodwidget/ui' +import { PageWizardProvider, WidgetVersion } from '@goodwidget/ui' +import packageJson from '../package.json' import { GovernanceOnboardingFlow } from './onboarding/GovernanceOnboardingFlow' import { DEFAULT_FINAL_ACTIONS, DEFAULT_TRANSACTION_STEPS, ONBOARDING_STEPS } from './onboarding/constants' import { HOUSE_COPY } from './onboarding/copy' @@ -63,6 +64,7 @@ export function GovernanceOnboardingWidget({ onFinalActionPress={onFinalActionPress} dataTestId={dataTestId} /> + ) } diff --git a/packages/governance-widget/src/GovernanceWidget.tsx b/packages/governance-widget/src/GovernanceWidget.tsx index 77318584..cbe8b268 100644 --- a/packages/governance-widget/src/GovernanceWidget.tsx +++ b/packages/governance-widget/src/GovernanceWidget.tsx @@ -12,6 +12,8 @@ import { XStack, YStack, } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { useTheme } from 'tamagui' import { AlignmentVotingProposalCard } from './AlignmentVotingProposalCard' import { BalanceCard } from './BalanceCard' @@ -759,6 +761,7 @@ export function GovernanceWidget({ ) : ( )} + ) } diff --git a/packages/governance-widget/tsconfig.build.json b/packages/governance-widget/tsconfig.build.json index 5a24989c..7e89cd4c 100644 --- a/packages/governance-widget/tsconfig.build.json +++ b/packages/governance-widget/tsconfig.build.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src" + "rootDir": "." }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/governance-widget/tsconfig.json b/packages/governance-widget/tsconfig.json index c2f740fe..87d73765 100644 --- a/packages/governance-widget/tsconfig.json +++ b/packages/governance-widget/tsconfig.json @@ -2,11 +2,11 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/ui": ["../ui/src/index.ts"], "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/staking-migration-widget/src/StakingMigrationWidget.tsx b/packages/staking-migration-widget/src/StakingMigrationWidget.tsx index 55e43e60..28fd9e22 100644 --- a/packages/staking-migration-widget/src/StakingMigrationWidget.tsx +++ b/packages/staking-migration-widget/src/StakingMigrationWidget.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useMemo } from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { Text, ToastContainer, YStack } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { MigrationProgressTimeline } from './MigrationProgressTimeline' import { MigrationSummaryCard } from './MigrationSummaryCard' import { MigrationSecondaryCard } from './migrationWidgetComponents' @@ -155,6 +157,7 @@ export function StakingMigrationWidget({ adapterFactory={adapterFactory} /> + ) } diff --git a/packages/staking-migration-widget/tsconfig.build.json b/packages/staking-migration-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/staking-migration-widget/tsconfig.build.json +++ b/packages/staking-migration-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/staking-migration-widget/tsconfig.json b/packages/staking-migration-widget/tsconfig.json index 006b6f40..5f693f93 100644 --- a/packages/staking-migration-widget/tsconfig.json +++ b/packages/staking-migration-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -10,5 +10,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/streaming-widget/src/StreamingWidget.tsx b/packages/streaming-widget/src/StreamingWidget.tsx index fbc25e39..5e9d2c1a 100644 --- a/packages/streaming-widget/src/StreamingWidget.tsx +++ b/packages/streaming-widget/src/StreamingWidget.tsx @@ -2,6 +2,8 @@ import React from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { ToastContainer } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { useStreamingAdapter } from './adapter' import { StreamingWidgetView } from './components/StreamingWidgetView' import type { @@ -47,6 +49,7 @@ export function StreamingWidgetPreview({ initialStreamsFormOpen={initialStreamsFormOpen} /> + ) } @@ -68,6 +71,7 @@ export function StreamingWidget({ > + ) } diff --git a/packages/streaming-widget/tsconfig.build.json b/packages/streaming-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/streaming-widget/tsconfig.build.json +++ b/packages/streaming-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/streaming-widget/tsconfig.json b/packages/streaming-widget/tsconfig.json index 006b6f40..5f693f93 100644 --- a/packages/streaming-widget/tsconfig.json +++ b/packages/streaming-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/core/*": ["../core/src/*"], @@ -10,5 +10,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx b/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx index 7218b043..4c9668f4 100644 --- a/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx +++ b/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx @@ -3,6 +3,8 @@ import { GoodWidgetProvider, useWallet } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { CitizenClaimWidget } from '@goodwidget/citizen-claim-widget' import { Card, Heading, Text, ToastContainer, YStack } from '@goodwidget/ui' +import packageJson from '../package.json' +import { WidgetVersion } from '@goodwidget/ui' import { CampaignHeader } from './components/CampaignHeader' import { FaqAccordion } from './components/FaqAccordion' import { LeaderboardSummary } from './components/LeaderboardSummary' @@ -308,6 +310,7 @@ export function SuperfluidCampaignWidgetWithClient({ /> + ) } diff --git a/packages/superfluid-campaign-widget/tsconfig.build.json b/packages/superfluid-campaign-widget/tsconfig.build.json index 54871c40..60a46961 100644 --- a/packages/superfluid-campaign-widget/tsconfig.build.json +++ b/packages/superfluid-campaign-widget/tsconfig.build.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/superfluid-campaign-widget/tsconfig.json b/packages/superfluid-campaign-widget/tsconfig.json index a720cb71..793b9eca 100644 --- a/packages/superfluid-campaign-widget/tsconfig.json +++ b/packages/superfluid-campaign-widget/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", + "rootDir": ".", "paths": { "@goodwidget/core": ["../core/src/index.ts"], "@goodwidget/ui": ["../ui/src/index.ts"], @@ -10,5 +10,5 @@ "react-native": ["./node_modules/react-native-web"] } }, - "include": ["src"] + "include": ["src", "package.json"] } diff --git a/packages/ui/src/components/WidgetVersion.tsx b/packages/ui/src/components/WidgetVersion.tsx new file mode 100644 index 00000000..e77456e8 --- /dev/null +++ b/packages/ui/src/components/WidgetVersion.tsx @@ -0,0 +1,24 @@ +import { YStack } from 'tamagui' +import { Text } from './Text' + +export interface WidgetVersionProps { + /** Widget package version, taken from the widget's own package.json. */ + version: string + /** Optional test id override. */ + testId?: string +} + +/** + * Small, muted version label rendered at the bottom-right corner of a widget. + * It sits in normal flow below the widget content (right-aligned) so it never + * overlaps or shifts the widget layout. + */ +export function WidgetVersion({ version, testId = 'widget-version' }: WidgetVersionProps) { + return ( + + + {`v${version}`} + + + ) +} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 4d1e88fb..d0671280 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -44,6 +44,8 @@ export { ScrollArea } from './components-test/ScrollArea' // Typography export { Heading } from './components/Heading' export { Text } from './components/Text' +export { WidgetVersion } from './components/WidgetVersion' +export type { WidgetVersionProps } from './components/WidgetVersion' export { Anchor } from './components/Anchor' // Inputs From 85089b2e3c4f37f138a085fcf6934bb4e53c4030 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:25:29 +0000 Subject: [PATCH 3/4] Apply remaining changes Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com> --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 284c8dc6..f8c548c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,6 +92,7 @@ GoodWidget/ - Treat public component names and theme targets as API surface (renaming is a breaking change for integrators). - Mirror the issue checklist in the PR body. +- Render `` (from `@goodwidget/ui`) in every widget; see `docs/widget-author-instructions.md`. - Preserve existing code comments when refactoring files unless explicitly asked to remove them. From f68e641716255c028ba4151f0304308ea7171381 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Oct 2026 12:57:53 +0000 Subject: [PATCH 4/4] Import named version from package.json and add version UI tests Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com> --- AGENTS.md | 2 +- docs/widget-author-instructions.md | 6 +++--- packages/ai-credits-widget/src/AiCreditsWidget.tsx | 4 ++-- .../citizen-claim-widget/src/CitizenClaimWidget.tsx | 4 ++-- .../claim-widget-theme-demo/src/ClaimWidget.tsx | 4 ++-- packages/gooddata-widget/src/GoodDataWidget.tsx | 4 ++-- .../goodreserve-widget/src/GoodReserveWidget.tsx | 4 ++-- .../src/GovernanceOnboardingWidget.tsx | 4 ++-- packages/governance-widget/src/GovernanceWidget.tsx | 4 ++-- .../src/StakingMigrationWidget.tsx | 4 ++-- packages/streaming-widget/src/StreamingWidget.tsx | 6 +++--- .../src/SuperfluidCampaignWidget.tsx | 4 ++-- tests/widgets/ai-credits-widget/version.spec.ts | 13 +++++++++++++ tests/widgets/citizen-claim-widget/version.spec.ts | 13 +++++++++++++ tests/widgets/goodreserve-widget/version.spec.ts | 11 +++++++++++ tests/widgets/governance-widget/version.spec.ts | 13 +++++++++++++ .../staking-migration-widget/version.spec.ts | 13 +++++++++++++ tests/widgets/streaming-widget/version.spec.ts | 13 +++++++++++++ .../superfluid-campaign-widget/version.spec.ts | 13 +++++++++++++ 19 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 tests/widgets/ai-credits-widget/version.spec.ts create mode 100644 tests/widgets/citizen-claim-widget/version.spec.ts create mode 100644 tests/widgets/goodreserve-widget/version.spec.ts create mode 100644 tests/widgets/governance-widget/version.spec.ts create mode 100644 tests/widgets/staking-migration-widget/version.spec.ts create mode 100644 tests/widgets/streaming-widget/version.spec.ts create mode 100644 tests/widgets/superfluid-campaign-widget/version.spec.ts diff --git a/AGENTS.md b/AGENTS.md index f8c548c8..4fe02350 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,7 @@ GoodWidget/ - Treat public component names and theme targets as API surface (renaming is a breaking change for integrators). - Mirror the issue checklist in the PR body. -- Render `` (from `@goodwidget/ui`) in every widget; see `docs/widget-author-instructions.md`. +- Render `` (from `@goodwidget/ui`) in every widget; see `docs/widget-author-instructions.md`. - Preserve existing code comments when refactoring files unless explicitly asked to remove them. diff --git a/docs/widget-author-instructions.md b/docs/widget-author-instructions.md index 78097052..60943d8c 100644 --- a/docs/widget-author-instructions.md +++ b/docs/widget-author-instructions.md @@ -306,9 +306,9 @@ last child of the widget's root (inside `GoodWidgetProvider`) so it never overla ```tsx import { WidgetVersion } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' - + ``` The widget's `tsconfig.json` and `tsconfig.build.json` must use `"rootDir": "."` and include @@ -328,7 +328,7 @@ Before requesting review: - Light and dark states were checked when the component supports both. - Mobile width was checked against the design reference. - Storybook screenshots were refreshed when visual states changed. -- The widget renders ``. +- The widget renders ``. - Manual visual review was done against Figma/Stitch/reference screenshots. If a design seems impossible to express through the current system, do not work around it by diff --git a/packages/ai-credits-widget/src/AiCreditsWidget.tsx b/packages/ai-credits-widget/src/AiCreditsWidget.tsx index 3dd68c59..c9b7f6d9 100644 --- a/packages/ai-credits-widget/src/AiCreditsWidget.tsx +++ b/packages/ai-credits-widget/src/AiCreditsWidget.tsx @@ -20,7 +20,7 @@ import { createToast, updateToast, } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { needsWalletConnection, useAiCreditsAdapter } from './adapter' import { useAiCreditsHistory } from './useAiCreditsHistory' @@ -651,7 +651,7 @@ export function AiCreditsWidget({ showWalletControls={showWalletControls} /> - + ) diff --git a/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx b/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx index 9b347785..89782d65 100644 --- a/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx +++ b/packages/citizen-claim-widget/src/CitizenClaimWidget.tsx @@ -20,7 +20,7 @@ import { YStack, WidgetTabs, } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { SupportedChains } from '@goodsdks/citizen-sdk' import { getChainDisplayName, useCitizenClaimAdapter } from './adapter' @@ -689,7 +689,7 @@ export function CitizenClaimWidget({ onClaimError={onClaimError} initialTab={initialTab} /> - + ) } diff --git a/packages/claim-widget-theme-demo/src/ClaimWidget.tsx b/packages/claim-widget-theme-demo/src/ClaimWidget.tsx index e8f47222..146bcd2f 100644 --- a/packages/claim-widget-theme-demo/src/ClaimWidget.tsx +++ b/packages/claim-widget-theme-demo/src/ClaimWidget.tsx @@ -19,7 +19,7 @@ import { XStack, YStack, } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' const ClaimCard = createComponent(Card, { @@ -263,7 +263,7 @@ export function ClaimWidget({ defaultTheme={defaultTheme} > - + ) } diff --git a/packages/gooddata-widget/src/GoodDataWidget.tsx b/packages/gooddata-widget/src/GoodDataWidget.tsx index 0da42325..3916fbc1 100644 --- a/packages/gooddata-widget/src/GoodDataWidget.tsx +++ b/packages/gooddata-widget/src/GoodDataWidget.tsx @@ -11,7 +11,7 @@ import React from 'react' import { BrowserRouter, Link, Route, Routes, useParams } from 'react-router-dom' import { Heading, Text, WidgetVersion, YStack } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { getDashboard, listDashboards } from './dashboards/registry' const CONTENT_MAX_WIDTH = 1120 @@ -104,7 +104,7 @@ export function GoodDataWidget({ basename }: GoodDataWidgetProps = {}) { } /> - + ) diff --git a/packages/goodreserve-widget/src/GoodReserveWidget.tsx b/packages/goodreserve-widget/src/GoodReserveWidget.tsx index 93f844ed..748830f4 100644 --- a/packages/goodreserve-widget/src/GoodReserveWidget.tsx +++ b/packages/goodreserve-widget/src/GoodReserveWidget.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import { YStack } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import type { EIP1193Provider } from '@goodwidget/core' import { ReserveSwapView } from './ReserveSwapView' @@ -77,7 +77,7 @@ export function GoodReserveWidget({ mockState={mockState} preferredChainId={preferredChainId} /> - + ) diff --git a/packages/governance-widget/src/GovernanceOnboardingWidget.tsx b/packages/governance-widget/src/GovernanceOnboardingWidget.tsx index 36fe0989..fc6a651e 100644 --- a/packages/governance-widget/src/GovernanceOnboardingWidget.tsx +++ b/packages/governance-widget/src/GovernanceOnboardingWidget.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' import { PageWizardProvider, WidgetVersion } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { GovernanceOnboardingFlow } from './onboarding/GovernanceOnboardingFlow' import { DEFAULT_FINAL_ACTIONS, DEFAULT_TRANSACTION_STEPS, ONBOARDING_STEPS } from './onboarding/constants' import { HOUSE_COPY } from './onboarding/copy' @@ -64,7 +64,7 @@ export function GovernanceOnboardingWidget({ onFinalActionPress={onFinalActionPress} dataTestId={dataTestId} /> - + ) } diff --git a/packages/governance-widget/src/GovernanceWidget.tsx b/packages/governance-widget/src/GovernanceWidget.tsx index cbe8b268..8abc6858 100644 --- a/packages/governance-widget/src/GovernanceWidget.tsx +++ b/packages/governance-widget/src/GovernanceWidget.tsx @@ -12,7 +12,7 @@ import { XStack, YStack, } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { useTheme } from 'tamagui' import { AlignmentVotingProposalCard } from './AlignmentVotingProposalCard' @@ -761,7 +761,7 @@ export function GovernanceWidget({ ) : ( )} - + ) } diff --git a/packages/staking-migration-widget/src/StakingMigrationWidget.tsx b/packages/staking-migration-widget/src/StakingMigrationWidget.tsx index 28fd9e22..dc8600b3 100644 --- a/packages/staking-migration-widget/src/StakingMigrationWidget.tsx +++ b/packages/staking-migration-widget/src/StakingMigrationWidget.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo } from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { Text, ToastContainer, YStack } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { MigrationProgressTimeline } from './MigrationProgressTimeline' import { MigrationSummaryCard } from './MigrationSummaryCard' @@ -157,7 +157,7 @@ export function StakingMigrationWidget({ adapterFactory={adapterFactory} /> - + ) } diff --git a/packages/streaming-widget/src/StreamingWidget.tsx b/packages/streaming-widget/src/StreamingWidget.tsx index 5e9d2c1a..63c13bcf 100644 --- a/packages/streaming-widget/src/StreamingWidget.tsx +++ b/packages/streaming-widget/src/StreamingWidget.tsx @@ -2,7 +2,7 @@ import React from 'react' import { GoodWidgetProvider } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { ToastContainer } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { useStreamingAdapter } from './adapter' import { StreamingWidgetView } from './components/StreamingWidgetView' @@ -49,7 +49,7 @@ export function StreamingWidgetPreview({ initialStreamsFormOpen={initialStreamsFormOpen} /> - + ) } @@ -71,7 +71,7 @@ export function StreamingWidget({ > - + ) } diff --git a/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx b/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx index 4c9668f4..8bb42208 100644 --- a/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx +++ b/packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx @@ -3,7 +3,7 @@ import { GoodWidgetProvider, useWallet } from '@goodwidget/core' import type { EIP1193Provider } from '@goodwidget/core' import { CitizenClaimWidget } from '@goodwidget/citizen-claim-widget' import { Card, Heading, Text, ToastContainer, YStack } from '@goodwidget/ui' -import packageJson from '../package.json' +import { version } from '../package.json' import { WidgetVersion } from '@goodwidget/ui' import { CampaignHeader } from './components/CampaignHeader' import { FaqAccordion } from './components/FaqAccordion' @@ -310,7 +310,7 @@ export function SuperfluidCampaignWidgetWithClient({ /> - + ) } diff --git a/tests/widgets/ai-credits-widget/version.spec.ts b/tests/widgets/ai-credits-widget/version.spec.ts new file mode 100644 index 00000000..8379a534 --- /dev/null +++ b/tests/widgets/ai-credits-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/ai-credits-widget/package.json', 'utf-8'), +) as { version: string } + +test('AiCreditsWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-aicreditswidget-runtime-fixtures--disconnected&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/citizen-claim-widget/version.spec.ts b/tests/widgets/citizen-claim-widget/version.spec.ts new file mode 100644 index 00000000..aac6b1f9 --- /dev/null +++ b/tests/widgets/citizen-claim-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/citizen-claim-widget/package.json', 'utf-8'), +) as { version: string } + +test('CitizenClaimWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-citizenclaimwidget-runtime-fixtures--custodial-local-fixture&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/goodreserve-widget/version.spec.ts b/tests/widgets/goodreserve-widget/version.spec.ts new file mode 100644 index 00000000..43dca4f9 --- /dev/null +++ b/tests/widgets/goodreserve-widget/version.spec.ts @@ -0,0 +1,11 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/goodreserve-widget/package.json', 'utf-8'), +) as { version: string } + +test('GoodReserveWidget displays its package version', async ({ page }) => { + await page.goto('/iframe.html?id=widgets-goodreservewidget--interactive&viewMode=story') + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/governance-widget/version.spec.ts b/tests/widgets/governance-widget/version.spec.ts new file mode 100644 index 00000000..a0f68e2d --- /dev/null +++ b/tests/widgets/governance-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/governance-widget/package.json', 'utf-8'), +) as { version: string } + +test('GovernanceWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-governancewidget-runtime-fixtures--disconnected-dashboard&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/staking-migration-widget/version.spec.ts b/tests/widgets/staking-migration-widget/version.spec.ts new file mode 100644 index 00000000..346f63f2 --- /dev/null +++ b/tests/widgets/staking-migration-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/staking-migration-widget/package.json', 'utf-8'), +) as { version: string } + +test('StakingMigrationWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-stakingmigrationwidget-runtime-fixtures--ready&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/streaming-widget/version.spec.ts b/tests/widgets/streaming-widget/version.spec.ts new file mode 100644 index 00000000..62f0ea56 --- /dev/null +++ b/tests/widgets/streaming-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse(readFileSync('packages/streaming-widget/package.json', 'utf-8')) as { + version: string +} + +test('StreamingWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-streamingwidget-runtime-fixtures--populated-state&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +}) diff --git a/tests/widgets/superfluid-campaign-widget/version.spec.ts b/tests/widgets/superfluid-campaign-widget/version.spec.ts new file mode 100644 index 00000000..77fd7323 --- /dev/null +++ b/tests/widgets/superfluid-campaign-widget/version.spec.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { test, expect } from '@playwright/test' + +const { version } = JSON.parse( + readFileSync('packages/superfluid-campaign-widget/package.json', 'utf-8'), +) as { version: string } + +test('SuperfluidCampaignWidget displays its package version', async ({ page }) => { + await page.goto( + '/iframe.html?id=qa-superfluidcampaignwidget-runtime-fixtures--no-wallet-content&viewMode=story', + ) + await expect(page.getByTestId('widget-version')).toHaveText(`v${version}`, { timeout: 20_000 }) +})