Measure what's left of upstream; drop two unused components #117

Merged
jcoffey-dev merged 2 commits from phase1/measure into main 2026-10-18 23:50:49 +00:00
5 changed files with 106 additions and 141 deletions

No files matched your search

-32
View File
@@ -18,7 +18,6 @@
"@radix-ui/react-dropdown-menu": "^2.1.24",
"@radix-ui/react-label": "^2.1.15",
"@radix-ui/react-popover": "^1.1.23",
"@radix-ui/react-scroll-area": "^1.2.18",
"@radix-ui/react-select": "^2.3.7",
"@radix-ui/react-separator": "^1.1.15",
"@radix-ui/react-slot": "^1.3.3",
@@ -1286,37 +1285,6 @@
}
}
},
"node_modules/@radix-ui/react-scroll-area": {
"version": "1.2.18",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz",
"integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.3",
"@radix-ui/primitive": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.5",
"@radix-ui/react-context": "1.2.2",
"@radix-ui/react-direction": "1.1.4",
"@radix-ui/react-presence": "1.1.10",
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-use-callback-ref": "1.1.4",
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz",
+2 -2
View File
@@ -18,7 +18,8 @@
"i18n:check": "node tools/i18n-extract.mjs --check",
"notice:check": "node tools/notice-check.mjs",
"notice:fix": "node tools/notice-check.mjs --fix",
"manual": "node tools/build-manual.mjs --schema ../inbuxa-server/resources/schema/schema.json.gz --out ../inbuxa.org/docs-site/docs/reference"
"manual": "node tools/build-manual.mjs --schema ../inbuxa-server/resources/schema/schema.json.gz --out ../inbuxa.org/docs-site/docs/reference",
"divergence": "node tools/divergence.mjs"
},
"dependencies": {
"@daypicker/react": "^10.0.1",
@@ -31,7 +32,6 @@
"@radix-ui/react-dropdown-menu": "^2.1.24",
"@radix-ui/react-label": "^2.1.15",
"@radix-ui/react-popover": "^1.1.23",
"@radix-ui/react-scroll-area": "^1.2.18",
"@radix-ui/react-select": "^2.3.7",
"@radix-ui/react-separator": "^1.1.15",
"@radix-ui/react-slot": "^1.3.3",
-63
View File
@@ -1,63 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 Coffey Labs LLC
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
import * as React from 'react';
import { Tooltip as RechartsTooltip } from 'recharts';
import type { TooltipPayload } from 'recharts/types/state/tooltipSlice';
import { cn } from '@/lib/utils';
export const CHART_COLORS = [
'var(--chart-1)',
'var(--chart-2)',
'var(--chart-3)',
'var(--chart-4)',
'var(--chart-5)',
] as const;
export function getChartColor(index: number): string {
return CHART_COLORS[index % CHART_COLORS.length];
}
export function ChartContainer({ children, className }: { children: React.ReactNode; className?: string }) {
return <div className={cn('w-full', className)}>{children}</div>;
}
export function ChartTooltipContent({
active,
payload,
label,
formatter,
}: {
active?: boolean;
payload?: TooltipPayload;
label?: string;
formatter?: (value: number) => string;
}) {
if (!active || !payload?.length) return null;
return (
<div className="rounded-lg border bg-background p-2 shadow-md">
{label && <p className="mb-1 text-xs text-muted-foreground">{label}</p>}
{payload.map((entry, i) => (
<div key={i} className="flex items-center gap-2 text-sm">
<span
className="h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: entry.color ?? 'currentColor' }}
/>
<span className="text-muted-foreground">{entry.name ?? ''}</span>
<span className="ml-auto font-medium">
{formatter && typeof entry.value === 'number' ? formatter(entry.value) : String(entry.value ?? '')}
</span>
</div>
))}
</div>
);
}
export { RechartsTooltip };
-44
View File
@@ -1,44 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
import * as React from 'react';
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
import { cn } from '@/lib/utils';
const ScrollArea = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root ref={ref} className={cn('relative overflow-hidden', className)} {...props}>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = 'vertical', ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
'flex touch-none select-none transition-colors',
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env node
// SPDX-FileCopyrightText: 2026 Coffey Labs LLC
// SPDX-License-Identifier: AGPL-3.0-only
/**
* inbuxa: how much of the console is still upstream's, measured against
* BASE, the upstream release this fork started from. The console no longer
* takes upstream changes, and is being rewritten until none of upstream's
* code is left (inbuxa-drafts/specs/console-rewrite-plan.md); each PR of that
* work states how far it moved these numbers.
*
* node tools/divergence.mjs the summary, for HEAD
* node tools/divergence.mjs <ref> the summary, for another commit
* node tools/divergence.mjs --files also each file with upstream lines left
*
* Lines are counted from git's diff, without package-lock.json or binary
* files. "Not upstream's" is every line but upstream's: ours, and third-party
* code such as components taken from shadcn/ui.
*
* A file whose header named Stalwart Labs at BASE and no longer does has been
* rewritten, and none of its lines count as upstream's: the header only
* changes once the rewrite is done. A rewrite still shares some lines with
* upstream's file (blank lines, imports, closing brackets, and class lists
* both took from shadcn/ui), so the summary prints how many, to keep the
* claim checkable.
*/
import { execFileSync } from 'node:child_process';
// Upstream's web interface v1.0.11, the release this fork started from.
const BASE = process.env.DIVERGENCE_BASE || 'dc462b137fbee9c2193946a6db554ff20bcc9b07';
const EXCLUDE = [':!package-lock.json'];
const DUAL_HEADER = '^\\s*(\\*|//|#)\\s*SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL';
const UPSTREAM_HOLDER = 'SPDX-FileCopyrightText:.*Stalwart Labs';
const args = process.argv.slice(2);
const showFiles = args.includes('--files');
const ref = args.find((a) => !a.startsWith('--')) ?? 'HEAD';
const git = (...a) => execFileSync('git', a, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
const empty = git('hash-object', '-t', 'tree', '/dev/null').trim();
/** Per-file [added, deleted] between two trees, binary files left out. */
function numstat(from, to) {
const out = new Map();
for (const line of git('diff', '--numstat', '--no-renames', from, to, '--', '.', ...EXCLUDE).split('\n')) {
const [a, d, path] = line.split('\t');
if (!path || a === '-') continue;
out.set(path, [Number(a), Number(d)]);
}
return out;
}
const sum = (m, i) => [...m.values()].reduce((n, v) => n + v[i], 0);
/** Files at `at` whose lines match `pattern`, as a set of paths. */
function grepFiles(pattern, at) {
try {
return new Set(
git('grep', '-l', '-E', pattern, at, '--')
.split('\n')
.filter(Boolean)
.map((l) => l.slice(at.length + 1)),
);
} catch {
// git grep exits 1 when nothing matches
return new Set();
}
}
const baseFiles = numstat(empty, BASE);
const changes = numstat(BASE, ref);
const forkLines = sum(numstat(empty, ref), 0);
const upstreamLines = sum(baseFiles, 0);
const headed = grepFiles(UPSTREAM_HOLDER, BASE);
const stillUpstream = grepFiles(UPSTREAM_HOLDER, ref);
const left = [];
let rewritten = 0;
let shared = 0;
for (const [path, [lines]] of baseFiles) {
const n = lines - (changes.get(path)?.[1] ?? 0);
if (n <= 0) continue;
if (!headed.has(path) || stillUpstream.has(path)) left.push([n, path]);
else {
rewritten += 1;
shared += n;
}
}
left.sort((a, b) => b[0] - a[0] || a[1].localeCompare(b[1]));
const upstreamLeft = left.reduce((n, [l]) => n + l, 0);
const notUpstream = forkLines - upstreamLeft;
const dual = grepFiles(DUAL_HEADER, ref).size;
const pct = (a, b) => `${((100 * a) / b).toFixed(1)}%`;
const n = (x) => x.toLocaleString('en-US');
console.log(`Against upstream v1.0.11 (${BASE.slice(0, 7)}), at ${git('rev-parse', '--short', ref).trim()}:`);
console.log(` not upstream's: ${pct(notUpstream, forkLines)} (${n(notUpstream)} of ${n(forkLines)} lines)`);
console.log(` upstream lines left: ${n(upstreamLeft)} of ${n(upstreamLines)}, in ${n(left.length)} files`);
console.log(` dual-header files: ${n(dual)}`);
console.log(` rewritten files: ${n(rewritten)}, sharing ${n(shared)} lines with upstream's`);
if (showFiles) {
console.log('');
for (const [l, path] of left) console.log(`${String(l).padStart(6)} ${path}`);
}