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
272 changes: 133 additions & 139 deletions apps/docs/components/icons.tsx

Large diffs are not rendered by default.

272 changes: 133 additions & 139 deletions apps/sim/components/icons.tsx

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
"dev:sockets": "cd apps/realtime && bun run dev",
"dev:full": "bunx concurrently -n \"App,Realtime\" -c \"cyan,magenta\" \"cd apps/sim && bun run dev\" \"cd apps/realtime && bun run dev\"",
"dev:full:capped": "bunx concurrently -n \"App,Realtime\" -c \"cyan,magenta\" \"cd apps/sim && bun run dev:capped\" \"cd apps/realtime && bun run dev\"",
"test": "bun run test:setup && turbo run test",
"test": "bun run test:setup && bun run test:icon-path-precision && turbo run test",
"test:setup": "bun run --cwd packages/sim-setup test",
"test:icon-path-precision": "bunx vitest run scripts/check-icon-path-precision.test.ts",
"format": "turbo run format",
"format:check": "turbo run format:check",
"lint": "turbo run lint",
Expand Down Expand Up @@ -46,6 +47,7 @@
"check:canvas-sentences": "bun run apps/sim/scripts/check-canvas-sentences.ts --require-coverage",
"check:bare-icons": "bun run scripts/check-bare-icons.ts",
"check:icon-paths": "bun run scripts/check-icon-paths.ts",
"check:icon-path-precision": "bun run scripts/check-icon-path-precision.ts",
"check:migrations": "bun run scripts/check-migrations-safety.ts",
"check:native-typecheck": "bun run scripts/check-native-typecheck.ts",
"check:source-text": "bun run scripts/check-source-text.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/bubble-chat-delay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function BubbleChatDelay({ size = 24, width, height, ...props }: IconProp
{...props}
>
<path
d='M21.5 12C21.5 17.2467 17.2467 21.5 12 21.5C10.3719 21.5 8.8394 21.0904 7.5 20.3687C5.63177 19.362 4.37462 20.2979 3.26592 20.4658C3.09774 20.4913 2.93024 20.4302 2.80997 20.31C2.62741 20.1274 2.59266 19.8451 2.6935 19.6074C3.12865 18.5818 3.5282 16.6382 2.98341 15C2.6698 14.057 2.5 13.0483 2.5 12C2.5 6.75329 6.75329 2.5 12 2.5C17.2467 2.5 21.5 6.75329 21.5 12Z'
d='M21.5 12C21.5 17.25 17.25 21.5 12 21.5C10.37 21.5 8.84 21.09 7.5 20.37C5.63 19.36 4.37 20.3 3.27 20.47C3.1 20.49 2.93 20.43 2.81 20.31C2.63 20.13 2.59 19.85 2.69 19.61C3.13 18.58 3.53 16.64 2.98 15C2.67 14.06 2.5 13.05 2.5 12C2.5 6.75 6.75 2.5 12 2.5C17.25 2.5 21.5 6.75 21.5 12Z'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/document-attachment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function DocumentAttachment(props: SVGProps<SVGSVGElement>) {
{...props}
>
<path
d='M10.5 5.69V5.1C10.5 3.99 10.499 3.2 10.414 2.61C10.33 2.02 10.18 1.69 9.91 1.44C9.65 1.19 9.28 1.04 8.65 0.96C8.01 0.88 7.17 0.875 6 0.875H5.38C4.2 0.875 3.36 0.88 2.72 0.96C2.09 1.04 1.73 1.19 1.46 1.44C1.2 1.69 1.04 2.02 0.96 2.61C0.88 3.2 0.88 3.99 0.88 5.1V7.44C0.88 8.55 0.88 9.34 0.96 9.94C1.04 10.52 1.2 10.85 1.46 11.1C1.73 11.354 2.09 11.5 2.72 11.58C3.36 11.67 4.2 11.67 5.38 11.67H5.69C5.93 11.67 6.125 11.86 6.125 12.1C6.125 12.35 5.93 12.54 5.69 12.54H5.38C4.23 12.54 3.32 12.54 2.61 12.45C1.9 12.36 1.32 12.17 0.86 11.739C0.4 11.3 0.19 10.75 0.09 10.06C0 9.39 8.49e-07 8.52 8.99e-07 7.44V5.1C9.49e-07 4.02 0 3.16 0.09 2.48C0.19 1.79 0.4 1.24 0.86 0.8C1.32 0.37 1.9 0.18 2.61 0.09C3.32 0 4.23 2.75e-07 5.38 3.23e-07H6C7.15 3.71e-07 8.06 0 8.76 0.09C9.48 0.18 10.06 0.37 10.51 0.8C10.98 1.24 11.18 1.79 11.28 2.48C11.38 3.16 11.375 4.02 11.375 5.1V5.69C11.375 5.93 11.18 6.125 10.94 6.125C10.7 6.125 10.5 5.93 10.5 5.69Z'
d='M10.5 5.69V5.1C10.5 3.99 10.499 3.2 10.414 2.61C10.33 2.02 10.18 1.69 9.91 1.44C9.65 1.19 9.28 1.04 8.65 0.96C8.01 0.88 7.17 0.875 6 0.875H5.38C4.2 0.875 3.36 0.88 2.72 0.96C2.09 1.04 1.73 1.19 1.46 1.44C1.2 1.69 1.04 2.02 0.96 2.61C0.88 3.2 0.88 3.99 0.88 5.1V7.44C0.88 8.55 0.88 9.34 0.96 9.94C1.04 10.52 1.2 10.85 1.46 11.1C1.73 11.354 2.09 11.5 2.72 11.58C3.36 11.67 4.2 11.67 5.38 11.67H5.69C5.93 11.67 6.125 11.86 6.125 12.1C6.125 12.35 5.93 12.54 5.69 12.54H5.38C4.23 12.54 3.32 12.54 2.61 12.45C1.9 12.36 1.32 12.17 0.86 11.739C0.4 11.3 0.19 10.75 0.09 10.06C0 9.39 0 8.52 0 7.44V5.1C0 4.02 0 3.16 0.09 2.48C0.19 1.79 0.4 1.24 0.86 0.8C1.32 0.37 1.9 0.18 2.61 0.09C3.32 0 4.23 0 5.38 0H6C7.15 0 8.06 0 8.76 0.09C9.48 0.18 10.06 0.37 10.51 0.8C10.98 1.24 11.18 1.79 11.28 2.48C11.38 3.16 11.375 4.02 11.375 5.1V5.69C11.375 5.93 11.18 6.125 10.94 6.125C10.7 6.125 10.5 5.93 10.5 5.69Z'
fill='currentColor'
/>
<path
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function Download({ animate = false, className, ...props }: DownloadProps
{...props}
>
{/* tray — same as Upload */}
<path d='M0.75 12.75V16.75C0.75 17.8546 1.64543 18.75 2.75 18.75H17.75C18.8546 18.75 19.75 17.8546 19.75 16.75V12.75' />
<path d='M0.75 12.75V16.75C0.75 17.85 1.65 18.75 2.75 18.75H17.75C18.85 18.75 19.75 17.85 19.75 16.75V12.75' />
{/* stem — top to tray */}
<path d='M10.25 1.75V12.75' />
{/* arrowhead pointing down */}
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/duplicate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function Duplicate(props: SVGProps<SVGSVGElement>) {
aria-hidden='true'
{...props}
>
<path d='M14.25 0.75H2.75C1.64543 0.75 0.75 1.64543 0.75 2.75V14.25' />
<path d='M14.25 0.75H2.75C1.65 0.75 0.75 1.65 0.75 2.75V14.25' />
<rect x='5.25' y='5.25' width='14' height='14' rx='2' />
</svg>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/mic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function Mic(props: SVGProps<SVGSVGElement>) {
{...props}
>
<rect x='7.25' y='0.75' width='6' height='12' rx='3' />
<path d='M3.75 9.25V10.25C3.75 13.8399 6.66015 16.75 10.25 16.75C13.8399 16.75 16.75 13.8399 16.75 10.25V9.25' />
<path d='M3.75 9.25V10.25C3.75 13.84 6.66 16.75 10.25 16.75C13.84 16.75 16.75 13.84 16.75 10.25V9.25' />
<path d='M10.25 16.75V19.25' />
</svg>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/icons/upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function Upload(props: SVGProps<SVGSVGElement>) {
aria-hidden='true'
{...props}
>
<path d='M0.75 12.75V16.75C0.75 17.8546 1.64543 18.75 2.75 18.75H17.75C18.8546 18.75 19.75 17.8546 19.75 16.75V12.75' />
<path d='M0.75 12.75V16.75C0.75 17.85 1.65 18.75 2.75 18.75H17.75C18.85 18.75 19.75 17.85 19.75 16.75V12.75' />
<path d='M10.25 14.75V1.75' />
<path d='M5.25 6.75L10.25 1.75L15.25 6.75' />
</svg>
Expand Down
123 changes: 123 additions & 0 deletions scripts/check-icon-path-precision.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { describe, expect, it } from 'vitest'
import {
analyzeIconSource,
effectiveFractionDigits,
findPrecisionCandidates,
} from './check-icon-path-precision'

const FIXTURE_PATH = '/repo/packages/emcn/src/icons/fixture.tsx'

describe('icon path precision audit', () => {
it('accepts the three-decimal boundary and ignores geometry outside literal paths', () => {
const source = `
const dynamicPath = 'M0.1234 1'
const unrelated = "d='M0.1234 1'"
export function SafeIcon() {
return (
<svg viewBox='0 0 10.1234 10' transform='scale(0.16624)'>
<path d='M.5 1.200L1.2e1 2' />
<path d={dynamicPath} />
</svg>
)
}
`

expect(analyzeIconSource(source, FIXTURE_PATH)).toEqual({
candidates: [],
invalidExceptions: [],
})
})

it('finds ordinary decimals and exponents finer than a thousandth', () => {
const source = `
export function PreciseIcon() {
return <svg><path d='M0.1234 1e-4L2.345 5' /></svg>
}
`

const candidates = findPrecisionCandidates(source, FIXTURE_PATH)
expect(candidates).toHaveLength(1)
expect(candidates[0]).toMatchObject({
icon: 'PreciseIcon',
maxFractionDigits: 4,
offendingNumbers: ['0.1234', '1e-4'],
})
expect(effectiveFractionDigits('2.13949e-05')).toBe(10)
})

it('checks literal JSX expressions and static template literals', () => {
const source = `
export function ExpressionIcon() {
return (
<svg>
<path d={'M0.1234 1'} />
<path d={\`M2.3456 3\`} />
</svg>
)
}
`

expect(findPrecisionCandidates(source, FIXTURE_PATH)).toHaveLength(2)
})

it('accepts a reasoned TSDoc exception on the immediately following path', () => {
const source = `
export function PreciseBrandIcon() {
return (
<svg>
{/**
* svg-path-precision-exception: Rounding visibly distorts the provider-authored mark.
*/}
<path d='M0.1234 1' />
</svg>
)
}
`

expect(analyzeIconSource(source, FIXTURE_PATH)).toEqual({
candidates: [],
invalidExceptions: [],
})
})

it('rejects exceptions without a reason and exceptions on already-clean paths', () => {
const missingReason = `
export function PreciseIcon() {
return <svg>{/** svg-path-precision-exception: */}<path d='M0.1234 1' /></svg>
}
`
const unnecessary = `
export function CleanIcon() {
return <svg>{/** svg-path-precision-exception: Keep detail. */}<path d='M0.12 1' /></svg>
}
`

const missingReasonAnalysis = analyzeIconSource(missingReason, FIXTURE_PATH)
expect(missingReasonAnalysis.candidates).toHaveLength(1)
expect(missingReasonAnalysis.invalidExceptions[0]?.message).toContain('specific reason')

const unnecessaryAnalysis = analyzeIconSource(unnecessary, FIXTURE_PATH)
expect(unnecessaryAnalysis.candidates).toEqual([])
expect(unnecessaryAnalysis.invalidExceptions[0]?.message).toContain('unnecessary')
})

it('requires a reasoned exception to remain while its precise path remains', () => {
const excepted = `
export function PreciseBrandIcon() {
return (
<svg>
{/** svg-path-precision-exception: Rounding visibly distorts the brand mark. */}
<path d='M0.1234 1' />
</svg>
)
}
`
const exceptionRemoved = excepted.replace(
'{/** svg-path-precision-exception: Rounding visibly distorts the brand mark. */}',
''
)

expect(findPrecisionCandidates(excepted, FIXTURE_PATH)).toEqual([])
expect(findPrecisionCandidates(exceptionRemoved, FIXTURE_PATH)).toHaveLength(1)
})
})
Loading
Loading