Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ jobs:
- name: Install pnpm
run: npm install --global pnpm@11.10.0

- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1

- name: Set up just
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4

- name: Install dependencies
run: pnpm install --frozen-lockfile
run: just setup

- name: Run static and build checks
run: pnpm run check
run: just check

- name: Install Chromium
run: pnpm exec playwright install --with-deps chromium

- name: Qualify packed package and browser
run: pnpm run qualify
run: just test-integration
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ npm-debug.log*
# Misc
.DS_Store
*.pem

# Python tooling cache
__pycache__/
33 changes: 33 additions & 0 deletions .loc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"exceptions": {
"apps/playground/src/App.tsx": {
"max": 3413,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"apps/playground/src/mining-pages.tsx": {
"max": 720,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"packages/ui/src/blocks/campaign-schedule-matrix.tsx": {
"max": 994,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"packages/ui/src/blocks/creator-transfer.tsx": {
"max": 626,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"packages/ui/src/blocks/navigation-panel.tsx": {
"max": 1349,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"packages/ui/src/components/line-item-graph.tsx": {
"max": 896,
"reason": "Existing component and demo responsibilities; extract cohesive modules in the follow-up refactor. No growth."
},
"tests/e2e/qualification.spec.ts": {
"max": 1540,
"reason": "Existing browser qualification scenarios; split by tested feature in the follow-up refactor. No growth."
}
},
"excluded": {}
}
26 changes: 26 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"plugins": ["typescript", "unicorn", "oxc", "react", "jsx-a11y"],
"categories": {
"correctness": "error"
},
"rules": {
"complexity": ["error", 12],
"max-lines-per-function": [
"error",
{
"max": 100,
"skipBlankLines": true,
"skipComments": true
}
],
"react/rules-of-hooks": "error",
"react/exhaustive-deps": "error",
"jsx-a11y/label-has-associated-control": [
"error",
{
"controlComponents": ["Input"]
}
]
},
"ignorePatterns": ["**/dist/**", "output/**"]
}
8 changes: 7 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ node_modules/
.turbo/
coverage/
pnpm-lock.yaml
.pnpm-store/
.pnpm-store/
output/
.ask-pro/
.playwright-cli/
.impeccable/
# Upstream-owned copy from meta; preserve its original formatting.
docs/brand_assets/NEXTIDE_BRAND_AGENT_GUIDE.md
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ This repo is the shared `@nextide/ui` package. The playground exists only as a l
- Keep React Doctor healthy from time to time with `npx react-doctor@latest`; treat warnings as cleanup candidates before they pile up.
- Do not assume backwards compatibility. If compatibility expectations are unclear, ask before locking the direction.
- Never write product decisions, implementation details, API routing, fallback mechanics, or other internal plumbing into user-facing or agent-facing text. UI copy, notices, docs, prompts, labels, and tool messages should describe the user goal, required action, or visible state, not how the system is internally wired.

Use `just setup` and `just check` for the shared development baseline; see README.md for focused tests and browser prerequisites. Keep named LOC budgets and function debt exceptions from growing.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@ Shared Nextide shadcn/ui components with a Vite playground.

Run `pnpm dev`, then open [http://127.0.0.1:5174](http://127.0.0.1:5174) to view the component catalogue.

## Development checks

Install Node 22.14+, pnpm 11.10.0, `just`, and uv. On Windows, add Git for
Windows' `bin` directory to PATH so `just` can find `sh`.

- `just setup` installs the frozen workspace lockfile.
- `just check` runs Prettier, Oxlint, existing ESLint, the supply-chain watchlist,
LOC budgets, strict TypeScript checks, and packed-consumer qualification.
- `just fmt`, `just fmt-check`, `just lint`, and `just typecheck` run separately.
- `just test` runs the packed-consumer check through Node's test runner; native
flags and file selection work, e.g. `just test --test-reporter=spec scripts/check-packed-consumer.mjs`.
This check builds and packs the library and installs an isolated consumer, so
it needs npm registry access. There is currently one local test file.
- `just test-integration` builds both workspaces and runs Playwright. First run
`pnpm exec playwright install chromium` (Linux CI also uses `--with-deps`).
Port 4173 must be free. Focus with `just test-integration -g "campaign schedule"`.

The [shared baseline](https://github.com/nextidemedia/meta/blob/main/docs/development-baseline.md)
sets complexity 12, function length 100, and source/test LOC limits. `.loc.json`
records exact legacy file budgets; reduce them after extraction and never grow
or regenerate them to accept new code. Narrow function comments record each
existing metric and the extraction debt; they are not permission to grow those
functions. Accessibility exceptions explain specific SVG, forwarded-prop, or
scroll-region semantics. Existing ESLint/React Hooks rules remain until parity
with Oxlint is verified. Oxlint 1.80.0 was the newest stable npm release at least
seven days old on 2026-09-08; the existing formatter and TypeScript remain pinned.

## Brand

The implementation guide from `nextide-saas-meta` is copied into `docs/brand_assets/NEXTIDE_BRAND_AGENT_GUIDE.md`. The large PDF and font zip stay in the meta repo for now.
Expand Down
2 changes: 1 addition & 1 deletion apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build": "tsc -b && vite build",
"lint": "eslint",
"format": "prettier --write \"**/*.{ts,tsx}\"",
"typecheck": "tsc --noEmit",
"typecheck": "tsc -b",
"preview": "vite preview"
},
"dependencies": {
Expand Down
6 changes: 6 additions & 0 deletions apps/playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,7 @@ function formatLargeMetricValue(value: number) {

const formatCompactMetricValue = formatCompactNumber

// oxlint-disable-next-line complexity, max-lines-per-function -- Legacy baseline: function `App` has a complexity of 33; The function `App` has too many lines (412); extract this function in the follow-up refactor.
export function App() {
const [settingsOpen, setSettingsOpen] = useState(false)
const [playgroundSessionActive, setPlaygroundSessionActive] = useState(true)
Expand Down Expand Up @@ -1735,6 +1736,7 @@ function KrakenDiscoveryPreview({ activeItemId }: { activeItemId: string }) {
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `IntelligencePlayground` has too many lines (219); extract this function in the follow-up refactor.
function IntelligencePlayground({
selectedCreatorIds,
dateRange,
Expand Down Expand Up @@ -1962,6 +1964,7 @@ function IntelligencePlayground({
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `DaedalusPlayground` has too many lines (293); extract this function in the follow-up refactor.
function DaedalusPlayground({
filterGroupId,
selectedFilterId,
Expand Down Expand Up @@ -2293,6 +2296,7 @@ function ChartDirection({
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `FoundationsPreview` has too many lines (154); extract this function in the follow-up refactor.
function FoundationsPreview() {
return (
<section className="grid gap-4 2xl:grid-cols-2">
Expand Down Expand Up @@ -2636,6 +2640,7 @@ function FoundationSwatch({
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `ComponentMatrix` has too many lines (562); extract this function in the follow-up refactor.
function ComponentMatrix({
density,
confidence,
Expand Down Expand Up @@ -3209,6 +3214,7 @@ function ComponentMatrix({
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `BlockPreview` has too many lines (238); extract this function in the follow-up refactor.
function BlockPreview({ motionScale }: { motionScale: number }) {
const navigationDrawer = useStagedDrawer({
durationMs: DRAWER_STAGE_DURATION_MS * motionScale,
Expand Down
3 changes: 3 additions & 0 deletions apps/playground/src/mining-pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const reportHistory: ReportRailItem[] = [
},
]

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `WebMiningPage` has too many lines (218); extract this function in the follow-up refactor.
function WebMiningPage() {
const [activeBookingId, setActiveBookingId] = useState("booking-2")
const [activePresetId, setActivePresetId] = useState("7d")
Expand Down Expand Up @@ -421,6 +422,7 @@ function WebMiningPage() {
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `KrakenMiningPage` has too many lines (137); extract this function in the follow-up refactor.
function KrakenMiningPage() {
const [activeRunId, setActiveRunId] = useState("run-2")
const activeRun = runRows.find((row) => row.id === activeRunId) ?? runRows[0]
Expand Down Expand Up @@ -563,6 +565,7 @@ function KrakenMiningPage() {
)
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `IntelligenceReportMiningPage` has too many lines (173); extract this function in the follow-up refactor.
function IntelligenceReportMiningPage() {
const [activeReportId, setActiveReportId] = useState("report-current")

Expand Down
26 changes: 26 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
set positional-arguments

setup:
pnpm install --frozen-lockfile

lint:
pnpm run lint
uv run --no-project --python 3.12 python scripts/check_loc.py

fmt:
pnpm run format

fmt-check:
pnpm run format:check

typecheck:
pnpm run typecheck

test *args="scripts/check-packed-consumer.mjs":
node --test "$@"

test-integration *args:
pnpm run build
pnpm exec playwright test "$@"

check: fmt-check lint typecheck test
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
"private": true,
"scripts": {
"build": "turbo build",
"check": "pnpm run lint && pnpm run typecheck && pnpm run build && pnpm run security:watchlist",
"check": "pnpm run lint && pnpm run typecheck && pnpm run build",
"dev": "turbo dev",
"lint": "turbo lint",
"format": "turbo format",
"lint": "pnpm exec oxlint --deny-warnings && turbo lint && pnpm run security:watchlist",
"format": "prettier --write .",
"qualify": "pnpm run test:package && pnpm --filter playground build && playwright test",
"typecheck": "turbo typecheck",
"security:watchlist": "node scripts/check-supply-chain.mjs",
"test:e2e": "pnpm run build && playwright test",
"test:package": "node scripts/check-packed-consumer.mjs"
"test:package": "node scripts/check-packed-consumer.mjs",
"format:check": "prettier --check ."
},
"devDependencies": {
"@axe-core/playwright": "4.12.1",
"@playwright/test": "1.61.1",
"oxlint": "1.80.0",
"prettier": "3.8.4",
"prettier-plugin-tailwindcss": "0.8.0",
"turbo": "2.9.18",
Expand Down
5 changes: 5 additions & 0 deletions packages/ui/src/blocks/campaign-schedule-matrix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const monthFormatter = new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
})

// oxlint-disable-next-line complexity, max-lines-per-function -- Legacy baseline: function `CampaignScheduleMatrix` has a complexity of 17; The function `CampaignScheduleMatrix` has too many lines (596); extract this function in the follow-up refactor.
function CampaignScheduleMatrix({
creators,
days,
Expand Down Expand Up @@ -327,6 +328,7 @@ function CampaignScheduleMatrix({
const node = scrollRef.current
if (!node) return

// oxlint-disable-next-line complexity -- Legacy baseline: function has a complexity of 21; extract this function in the follow-up refactor.
const handleWheel = (event: WheelEvent) => {
const target = event.target
const overBoard =
Expand Down Expand Up @@ -593,10 +595,13 @@ function CampaignScheduleMatrix({
</div>

{/* oxlint-disable-next-line react-doctor/click-events-have-key-events -- This region only cancels bubbled child clicks after a pointer drag; it is not an activation target. */}
{/* oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- Named scroll region supports keyboard scrolling and pointer panning; it is not an activation target. */}
<div
ref={scrollRef}
// oxlint-disable-next-line jsx-a11y/prefer-tag-over-role -- Named scroll region supports keyboard scrolling and pointer panning; it is not an activation target.
role="region"
aria-label="Campaign schedule timeline"
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- Named scroll region supports keyboard scrolling and pointer panning; it is not an activation target.
tabIndex={0}
data-zoom={zoom}
data-dragging={dragging ? "true" : "false"}
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/blocks/creator-transfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function animateRows(
})
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `CreatorTransfer` has too many lines (357); extract this function in the follow-up refactor.
function CreatorTransfer({
creators,
selectedIds,
Expand Down Expand Up @@ -264,6 +265,7 @@ function CreatorTransfer({
onSelectedIdsChange(nextAddedIds)
}

// oxlint-disable-next-line complexity -- Legacy baseline: function has a complexity of 13; extract this function in the follow-up refactor.
const transferCreator = (id: string, direction: "add" | "remove") => {
if (motionLocked) {
const alreadyQueued = queuedTransfers.current.some(
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/blocks/dashboard-filter-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type DashboardFilterItem = {
live?: boolean
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `DashboardFilterBar` has too many lines (208); extract this function in the follow-up refactor.
function DashboardFilterBar({
groups,
items,
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/blocks/evidence-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function EvidenceChoice({
label: React.ReactNode
}) {
return (
<span className="flex min-w-0 items-center justify-center gap-1.5 max-[24rem]:[&_svg]:hidden [&_svg]:size-3.5">
<span className="flex min-w-0 items-center justify-center gap-1.5 [&_svg]:size-3.5 max-[24rem]:[&_svg]:hidden">
{icon}
<span className="min-w-0 truncate">{label}</span>
</span>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/blocks/export-workbench.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type ExportSessionRow = {
status: React.ReactNode
}

// oxlint-disable-next-line max-lines-per-function -- Legacy baseline: The function `ExportWorkbench` has too many lines (163); extract this function in the follow-up refactor.
function ExportWorkbench({
schedule,
onScheduleChange,
Expand Down
25 changes: 10 additions & 15 deletions packages/ui/src/blocks/fit-leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,8 @@ function FitLeaderboard({
) : null}
</span>
</span>
<ScorePlate
label="Fit"
value={item.fit}
max={maxScore}
/>
<ScorePlate
label="Safety"
value={item.safety}
max={maxScore}
/>
<ScorePlate label="Fit" value={item.fit} max={maxScore} />
<ScorePlate label="Safety" value={item.safety} max={maxScore} />
<SentimentMeter
className="col-span-2 @xl:col-span-1"
value={item.sentiment}
Expand All @@ -110,12 +102,16 @@ function FitLeaderboard({
)
}

type ScorePlateTone = "success" | "processing" | "warning" | "danger" | "neutral"
type ScorePlateTone =
| "success"
| "processing"
| "warning"
| "danger"
| "neutral"

const scorePlateClasses: Record<ScorePlateTone, string> = {
success: "border-nextide-tide/35 [--score-plate:var(--nextide-tide)]",
processing:
"border-nextide-purple/35 [--score-plate:var(--nextide-purple)]",
processing: "border-nextide-purple/35 [--score-plate:var(--nextide-purple)]",
warning: "border-nextide-yellow/35 [--score-plate:var(--nextide-yellow)]",
danger: "border-nextide-red/35 [--score-plate:var(--nextide-red)]",
neutral: "border-nextide-line [--score-plate:var(--muted-foreground)]",
Expand All @@ -130,8 +126,7 @@ function ScorePlate({
value?: number
max: number
}) {
const valid =
typeof value === "number" && Number.isFinite(value) && max > 0
const valid = typeof value === "number" && Number.isFinite(value) && max > 0
const boundedValue = valid ? Math.max(0, Math.min(max, value)) : 0
const progress = max > 0 ? (boundedValue / max) * 100 : 0
const tone = scoreTone(value, max)
Expand Down
Loading
Loading