Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3f89367
fix: add inner right-side padding to dropdown chevrons
pastarita Mar 16, 2026
b7e25b2
fix: add CSS hyphenation breakpoints for long entity names
pastarita Mar 16, 2026
111ef3f
fix: complete badge variant mapping for all utility segments
pastarita Mar 16, 2026
106f641
style: overhaul header spacing and nav alignment
pastarita Mar 16, 2026
7c9de2f
style: conditional name clamping in grid-operators table
pastarita Mar 16, 2026
37b1abc
feat: add sort-direction chevrons to sort dropdown labels
pastarita Mar 16, 2026
791740d
feat: add more fields to power-plants and transmission-lines tables
pastarita Mar 16, 2026
ece78d0
feat: jurisdiction state-based highlighting in grid-operators table
pastarita Mar 16, 2026
ea227b5
feat: distinguish site changelog from industry data changelog
pastarita Mar 16, 2026
c9e343a
fix: stabilize Mapbox component mount/unmount lifecycle
pastarita Mar 16, 2026
ecdc89b
feat: redesign CommonGrid logo — G-in-C with dendritic graph
pastarita Mar 16, 2026
049d059
style: stack filter dropdowns vertically at narrow breakpoints
pastarita Mar 16, 2026
ba4b06d
feat: add EIA ID, BA Code, NERC, and website fields to grid-operators…
pastarita Mar 16, 2026
00353ab
feat: CMD+K search improvements — result counts and loading state
pastarita Mar 16, 2026
73c7db5
fix: improve service territory map bounds and zoom calculation
pastarita Mar 16, 2026
7da3bdb
feat: add search to changelog data feed
pastarita Mar 16, 2026
8689455
style: update favicon to match new G-in-C logo mark
pastarita Mar 16, 2026
ed04191
fix: add layout key to ExplorerMap for clean remount on mode switch
pastarita Mar 16, 2026
f37bc36
style: tighten mobile explorer tab labels
pastarita Mar 16, 2026
c206859
style: improve mobile menu styling and active state
pastarita Mar 16, 2026
64d1943
style: truncate DataSourceLink on narrow viewports
pastarita Mar 16, 2026
cf533a9
style: responsive search input sizing and result count visibility
pastarita Mar 16, 2026
a3129d5
feat: add page metadata to explore page
pastarita Mar 16, 2026
ff146f7
docs: add PR description template and welcome-pr analysis artifact
pastarita Mar 16, 2026
5a0db81
docs: refine PR template and description with research findings
pastarita Mar 16, 2026
2622431
docs: visual regression capture system specification
pastarita Mar 16, 2026
ca3ae6b
docs: add concrete implementation reference and tool landscape
pastarita Mar 16, 2026
f117566
feat: implement visual regression capture system
pastarita Mar 16, 2026
3494749
fix(vr): resolve 404 in BEFORE captures by running prebuild in worktree
pastarita Mar 17, 2026
ffecf2f
fix(vr): change ports to 4100/4101 and add port-availability check
pastarita Mar 17, 2026
bf7378b
Revert "feat: redesign CommonGrid logo — G-in-C with dendritic graph"
pastarita Mar 17, 2026
534f46d
docs: rewrite visual regression system as as-built reference with mer…
pastarita Mar 17, 2026
bc1fbcd
feat: add Codes column, changelog subtitle, Diffwatch alerting, and P…
pastarita Mar 17, 2026
f2f8df4
fix: remove double badge rendering on mobile grid operators list
pastarita Mar 17, 2026
7a1fccb
fix: render changelog subtitle as visible text below header
pastarita Mar 17, 2026
33bf95f
feat(diffwatch): add ordinal versioning to comparison outputs
pastarita Mar 17, 2026
aec3b14
feat(changelog): redesign header with pill toggle, filter fieldset, a…
pastarita Mar 17, 2026
c90cf52
docs: update PR description for changelog redesign and double-badge fix
pastarita Mar 17, 2026
b3a4a1b
feat(changelog): expand filter fieldset to wrap results when filterin…
pastarita Mar 17, 2026
e02b011
feat(diffwatch): overwrite prior versions instead of accumulating ord…
pastarita Mar 17, 2026
b22019f
fix(explorer): resolve layout ReferenceError and hide mapbox key in p…
pastarita Mar 17, 2026
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
8 changes: 4 additions & 4 deletions .claude/edges.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ The following components are available from @texturehq/edges:
- **SparklineCell** - Component
- **SplitPane** - Child panels
- **StaticMap** - Component
- **StatList** - Additional CSS classes */ className?: string; } // Helpers // Use the centralized formatting utility function formatValue(value: StatValue, formatter?: StatFormatter): React.ReactNode { // If value is already a React element, return it as-is (skip formatting) if (React.isValidElement(value)) { return value; } return formatComponentValue({ value, formatter, emptyClassName: "text-text-muted", emptyText: "—", }); } function getTone(item: StatItem): StatTone | undefined { // Check thresholds first if (item.thresholds && item.value !== null && item.value !== undefined) { for (const threshold of item.thresholds) { if (threshold.when(item.value)) { return threshold.tone; } } } // Fall back to explicit tone return item.tone; } const toneColors: Record<StatTone, string> = { neutral: "text-text-body", success: "text-feedback-success", warning: "text-feedback-warning", error: "text-feedback-error", info: "text-feedback-info", }; // StatRow Component // Constants for auto-truncation const LONG_STRING_THRESHOLD = 24; const LONG_STRING_TRUNCATE_LENGTH = 20; function StatRow({ item, dense, valueAlign, }: { item: StatItem; dense?: boolean; valueAlign?: StatAlign; }) { const tone = getTone(item); const toneClass = tone ? toneColors[tone] : ""; const isStacked = item.stackOnMobile; // Check if this is a long string that should be auto-truncated on mobile const isLongString = typeof item.value === "string" && item.value.length > LONG_STRING_THRESHOLD && !item.formatter && !React.isValidElement(item.value); // Get the formatted value (used for both truncated and full display) const formattedValue = formatValue(item.value, item.formatter); // Get the truncated value for mobile display // Note: We cast to string here because isLongString already verifies typeof item.value === "string" const truncatedValue = isLongString ? truncateMiddle(item.value as string, LONG_STRING_TRUNCATE_LENGTH) : null; const textToCopy = typeof item.copyable === "function" ? item.copyable(item.value) : String(item.value); // PII data attributes (only set if both piiType and piiEntity are provided) const piiAttrs = item.piiType && item.piiEntity ? { "data-pii-type": item.piiType, "data-pii-entity": item.piiEntity, } : {}; // For long strings, render both truncated (mobile) and full (desktop) versions // CSS classes control which is visible based on screen size // Tooltip content is wrapped in span with PII attributes to allow masking by Curtain extension const hasPiiAttrs = Object.keys(piiAttrs).length > 0; const valueDisplay = isLongString ? ( <> {/* Mobile: show truncated with tooltip */} <Tooltip content={hasPiiAttrs ? <span {...piiAttrs}>{String(item.value)}</span> : String(item.value)} > <span className={twMerge("md:hidden", item.href ? "hover:underline cursor-pointer" : "")} {...piiAttrs} > {truncatedValue} </span> </Tooltip> {/* Desktop: show full value
- **StatList** - Additional CSS classes */ className?: string; } // Helpers // Use the centralized formatting utility function formatValue(value: StatValue, formatter?: StatFormatter): React.ReactNode { // If value is already a React element, return it as-is (skip formatting) if (React.isValidElement(value)) { return value; } return formatComponentValue({ value, formatter, emptyClassName: "text-text-muted", emptyText: "—", }); } function getTone(item: StatItem): StatTone | undefined { // Check thresholds first if (item.thresholds && item.value !== null && item.value !== undefined) { for (const threshold of item.thresholds) { if (threshold.when(item.value)) { return threshold.tone; } } } // Fall back to explicit tone return item.tone; } const toneColors: Record<StatTone, string> = { neutral: "text-text-body", success: "text-feedback-success", warning: "text-feedback-warning", error: "text-feedback-error", info: "text-feedback-info", }; // StatRow Component // Constants for auto-truncation const LONG_STRING_THRESHOLD = 24; const LONG_STRING_TRUNCATE_LENGTH = 20; function StatRow({ item, dense, valueAlign }: { item: StatItem; dense?: boolean; valueAlign?: StatAlign }) { const tone = getTone(item); const toneClass = tone ? toneColors[tone] : ""; const isStacked = item.stackOnMobile; // Check if this is a long string that should be auto-truncated on mobile const isLongString = typeof item.value === "string" && item.value.length > LONG_STRING_THRESHOLD && !item.formatter && !React.isValidElement(item.value); // Get the formatted value (used for both truncated and full display) const formattedValue = formatValue(item.value, item.formatter); // Get the truncated value for mobile display // Note: We cast to string here because isLongString already verifies typeof item.value === "string" const truncatedValue = isLongString ? truncateMiddle(item.value as string, LONG_STRING_TRUNCATE_LENGTH) : null; const textToCopy = typeof item.copyable === "function" ? item.copyable(item.value) : String(item.value); // PII data attributes (only set if both piiType and piiEntity are provided) const piiAttrs = item.piiType && item.piiEntity ? { "data-pii-type": item.piiType, "data-pii-entity": item.piiEntity, } : {}; // For long strings, render both truncated (mobile) and full (desktop) versions // CSS classes control which is visible based on screen size // Tooltip content is wrapped in span with PII attributes to allow masking by Curtain extension const hasPiiAttrs = Object.keys(piiAttrs).length > 0; const valueDisplay = isLongString ? ( <> {/* Mobile: show truncated with tooltip */} <Tooltip content={hasPiiAttrs ? <span {...piiAttrs}>{String(item.value)}</span> : String(item.value)}> <span className={twMerge("md:hidden", item.href ? "hover:underline cursor-pointer" : "")} {...piiAttrs}> {truncatedValue} </span> </Tooltip> {/* Desktop: show full value
- **Switch** - Switch Toggle switch component for binary on/off states. Provides an accessible alternative to checkboxes for settings and preferences.
- **Tab** - Tab trigger element.
- **TabList** - TabList container.
Expand Down Expand Up @@ -451,7 +451,7 @@ Renders an Edges Button. When `href` is provided, renders a link-styled button u
- `size: Size`
- `style: React.CSSProperties`
- `target: string`
- `variant: | "default" | "brand" | "secondary" | "destructive" | "icon" | "link" | "unstyled" | "ghost" | "primary"`
- `variant: "default" | "brand" | "secondary" | "destructive" | "icon" | "link" | "unstyled" | "ghost" | "primary"`

---

Expand Down Expand Up @@ -1438,7 +1438,7 @@ PlaceSearch Location search component with autocomplete; emits a `Place` value

**Props:**
- `autoFocus: boolean`
- `countryRestrictions: string[]; // Array of ISO 3166-1 alpha-2 country codes proximity?: "ip" | [number, number]; // Either "ip" for IP-based location or [longitude, latitude] coordinates hideCountry?: boolean; // Hide country from results (useful when using countryRestrictions) showIcon?: boolean; // Show location type icons in results (default: true) className?: string`
- `countryRestrictions: string[]; // Array of ISO 3166-1 alpha-2 country codes proximity?: "ip" | [number, number]; // Either "ip" for IP-based location or [longitude, latitude] coordinates hideCountry?: boolean; // Hide country from results (useful when using countryRestrictions) showIcon?: boolean; // Show location type icons in results (default: true) useMobileTray?: boolean; // Whether to use a Tray (bottom sheet) on mobile devices instead of a Popover (default: true) className?: string`
- `defaultFilter: (textValue: string, inputValue: string) => boolean`
- `defaultSelectedKey: Key | null`
- `description: string`
Expand Down Expand Up @@ -1763,7 +1763,7 @@ Child panels
---

#### StatList
Additional CSS classes */ className?: string; } // Helpers // Use the centralized formatting utility function formatValue(value: StatValue, formatter?: StatFormatter): React.ReactNode { // If value is already a React element, return it as-is (skip formatting) if (React.isValidElement(value)) { return value; } return formatComponentValue({ value, formatter, emptyClassName: "text-text-muted", emptyText: "—", }); } function getTone(item: StatItem): StatTone | undefined { // Check thresholds first if (item.thresholds && item.value !== null && item.value !== undefined) { for (const threshold of item.thresholds) { if (threshold.when(item.value)) { return threshold.tone; } } } // Fall back to explicit tone return item.tone; } const toneColors: Record<StatTone, string> = { neutral: "text-text-body", success: "text-feedback-success", warning: "text-feedback-warning", error: "text-feedback-error", info: "text-feedback-info", }; // StatRow Component // Constants for auto-truncation const LONG_STRING_THRESHOLD = 24; const LONG_STRING_TRUNCATE_LENGTH = 20; function StatRow({ item, dense, valueAlign, }: { item: StatItem; dense?: boolean; valueAlign?: StatAlign; }) { const tone = getTone(item); const toneClass = tone ? toneColors[tone] : ""; const isStacked = item.stackOnMobile; // Check if this is a long string that should be auto-truncated on mobile const isLongString = typeof item.value === "string" && item.value.length > LONG_STRING_THRESHOLD && !item.formatter && !React.isValidElement(item.value); // Get the formatted value (used for both truncated and full display) const formattedValue = formatValue(item.value, item.formatter); // Get the truncated value for mobile display // Note: We cast to string here because isLongString already verifies typeof item.value === "string" const truncatedValue = isLongString ? truncateMiddle(item.value as string, LONG_STRING_TRUNCATE_LENGTH) : null; const textToCopy = typeof item.copyable === "function" ? item.copyable(item.value) : String(item.value); // PII data attributes (only set if both piiType and piiEntity are provided) const piiAttrs = item.piiType && item.piiEntity ? { "data-pii-type": item.piiType, "data-pii-entity": item.piiEntity, } : {}; // For long strings, render both truncated (mobile) and full (desktop) versions // CSS classes control which is visible based on screen size // Tooltip content is wrapped in span with PII attributes to allow masking by Curtain extension const hasPiiAttrs = Object.keys(piiAttrs).length > 0; const valueDisplay = isLongString ? ( <> {/* Mobile: show truncated with tooltip */} <Tooltip content={hasPiiAttrs ? <span {...piiAttrs}>{String(item.value)}</span> : String(item.value)} > <span className={twMerge("md:hidden", item.href ? "hover:underline cursor-pointer" : "")} {...piiAttrs} > {truncatedValue} </span> </Tooltip> {/* Desktop: show full value
Additional CSS classes */ className?: string; } // Helpers // Use the centralized formatting utility function formatValue(value: StatValue, formatter?: StatFormatter): React.ReactNode { // If value is already a React element, return it as-is (skip formatting) if (React.isValidElement(value)) { return value; } return formatComponentValue({ value, formatter, emptyClassName: "text-text-muted", emptyText: "—", }); } function getTone(item: StatItem): StatTone | undefined { // Check thresholds first if (item.thresholds && item.value !== null && item.value !== undefined) { for (const threshold of item.thresholds) { if (threshold.when(item.value)) { return threshold.tone; } } } // Fall back to explicit tone return item.tone; } const toneColors: Record<StatTone, string> = { neutral: "text-text-body", success: "text-feedback-success", warning: "text-feedback-warning", error: "text-feedback-error", info: "text-feedback-info", }; // StatRow Component // Constants for auto-truncation const LONG_STRING_THRESHOLD = 24; const LONG_STRING_TRUNCATE_LENGTH = 20; function StatRow({ item, dense, valueAlign }: { item: StatItem; dense?: boolean; valueAlign?: StatAlign }) { const tone = getTone(item); const toneClass = tone ? toneColors[tone] : ""; const isStacked = item.stackOnMobile; // Check if this is a long string that should be auto-truncated on mobile const isLongString = typeof item.value === "string" && item.value.length > LONG_STRING_THRESHOLD && !item.formatter && !React.isValidElement(item.value); // Get the formatted value (used for both truncated and full display) const formattedValue = formatValue(item.value, item.formatter); // Get the truncated value for mobile display // Note: We cast to string here because isLongString already verifies typeof item.value === "string" const truncatedValue = isLongString ? truncateMiddle(item.value as string, LONG_STRING_TRUNCATE_LENGTH) : null; const textToCopy = typeof item.copyable === "function" ? item.copyable(item.value) : String(item.value); // PII data attributes (only set if both piiType and piiEntity are provided) const piiAttrs = item.piiType && item.piiEntity ? { "data-pii-type": item.piiType, "data-pii-entity": item.piiEntity, } : {}; // For long strings, render both truncated (mobile) and full (desktop) versions // CSS classes control which is visible based on screen size // Tooltip content is wrapped in span with PII attributes to allow masking by Curtain extension const hasPiiAttrs = Object.keys(piiAttrs).length > 0; const valueDisplay = isLongString ? ( <> {/* Mobile: show truncated with tooltip */} <Tooltip content={hasPiiAttrs ? <span {...piiAttrs}>{String(item.value)}</span> : String(item.value)}> <span className={twMerge("md:hidden", item.href ? "hover:underline cursor-pointer" : "")} {...piiAttrs}> {truncatedValue} </span> </Tooltip> {/* Desktop: show full value

**Imports:**
- `import { StatList } from "@texturehq/edges"`
Expand Down
Loading
Loading