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
1 change: 1 addition & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"rules": {
"conciv/banned-vocabulary": "error",
"conciv/lucide-per-icon": "error",
"conciv/no-comments": "error",
"conciv/no-locator-poll": "error",
"conciv/solid-test-render": "error",
Expand Down
4 changes: 3 additions & 1 deletion apps/conciv/src/composer/actions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {Show, createSignal, type JSX} from 'solid-js'
import {useQuery, useMutation} from '@tanstack/solid-query'
import {TooltipIconButton} from '@conciv/ui-kit-system'
import {Crosshair, FoldVertical, SquarePen} from 'lucide-solid'
import Crosshair from 'lucide-solid/icons/crosshair'
import FoldVertical from 'lucide-solid/icons/fold-vertical'
import SquarePen from 'lucide-solid/icons/square-pen'
import {getHostApi} from '@conciv/extension'
import type {Grab} from '@conciv/grab'
import {useAppData} from '../app/context.js'
Expand Down
4 changes: 3 additions & 1 deletion apps/conciv/src/composer/launch-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {Match, Switch, splitProps, type JSX} from 'solid-js'
import {Menu, TooltipIconButtonSlot} from '@conciv/ui-kit-system'
import {ClipboardCopy, RotateCw, SquareTerminal} from 'lucide-solid'
import ClipboardCopy from 'lucide-solid/icons/clipboard-copy'
import RotateCw from 'lucide-solid/icons/rotate-cw'
import SquareTerminal from 'lucide-solid/icons/square-terminal'

const RETRY_LABEL = 'Try again'

Expand Down
2 changes: 1 addition & 1 deletion apps/conciv/src/composer/model-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createEffect, createMemo, For, Match, on, Show, splitProps, Switch, type JSX} from 'solid-js'
import {useQuery, useMutation} from '@tanstack/solid-query'
import {Button} from '@conciv/ui-kit-system'
import {RotateCw} from 'lucide-solid'
import RotateCw from 'lucide-solid/icons/rotate-cw'
import {ModelSelector, useModelSelectorContext, type ModelOption} from '@conciv/ui-kit-chat'
import type {HarnessModelInfo} from '@conciv/protocol/chat-types'
import {useAnnounce, useAppData, useRpc} from '../app/context.js'
Expand Down
6 changes: 5 additions & 1 deletion apps/conciv/src/composer/session-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import {createSignal, createEffect, For, Show, onMount, type JSX} from 'solid-js
import {Button, Combobox, TooltipIconButton} from '@conciv/ui-kit-system'
import {useListCollection} from '@ark-ui/solid/combobox'
import {useQuery, useMutation} from '@tanstack/solid-query'
import {Check, ChevronDown, Sparkles, SquarePen, Plus} from 'lucide-solid'
import Check from 'lucide-solid/icons/check'
import ChevronDown from 'lucide-solid/icons/chevron-down'
import Sparkles from 'lucide-solid/icons/sparkles'
import SquarePen from 'lucide-solid/icons/square-pen'
import Plus from 'lucide-solid/icons/plus'
import type {SessionMeta} from '@conciv/contract'
import {useAnnounce, useAppData, useRpc} from '../app/context.js'

Expand Down
2 changes: 1 addition & 1 deletion apps/conciv/src/pane/grab-reference.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Show, type JSX} from 'solid-js'
import {X} from 'lucide-solid'
import X from 'lucide-solid/icons/x'
import {TooltipIconButton} from '@conciv/ui-kit-system'
import type {GrabPreview, Grab} from '@conciv/grab'
import {sourceLabel} from './grab-source-label.js'
Expand Down
3 changes: 2 additions & 1 deletion apps/conciv/src/pane/indicators.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {type JSX} from 'solid-js'
import {Dynamic} from 'solid-js/web'
import {SquarePen, FoldVertical} from 'lucide-solid'
import SquarePen from 'lucide-solid/icons/square-pen'
import FoldVertical from 'lucide-solid/icons/fold-vertical'
import {Progress} from '@conciv/ui-kit-system'

const DIVIDER =
Expand Down
6 changes: 5 additions & 1 deletion apps/conciv/src/pane/pane-composer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {Show, type Component, type JSX} from 'solid-js'
import {Dynamic} from 'solid-js/web'
import {ArrowUp, Clock, Paperclip, RefreshCw, Square} from 'lucide-solid'
import ArrowUp from 'lucide-solid/icons/arrow-up'
import Clock from 'lucide-solid/icons/clock'
import Paperclip from 'lucide-solid/icons/paperclip'
import RefreshCw from 'lucide-solid/icons/refresh-cw'
import Square from 'lucide-solid/icons/square'
import {ComposerPrimitive, QueueItem, AttachmentUI, type AttachmentAdapter} from '@conciv/ui-kit-chat'
import {TooltipIconButtonSlot} from '@conciv/ui-kit-system'
import type {WebStorage} from '@conciv/storage-history'
Expand Down
2 changes: 1 addition & 1 deletion apps/conciv/src/pane/tool-fallback-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {JSX} from 'solid-js'
import {Wrench} from 'lucide-solid'
import Wrench from 'lucide-solid/icons/wrench'
import {ToolCard, ToolFallback, ToolFallbackPrimitive} from '@conciv/ui-kit-chat/tools'
import type {ToolCardProps} from '@conciv/protocol/tool-view-types'

Expand Down
4 changes: 3 additions & 1 deletion apps/conciv/src/routes/panel.$sessionId.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {Outlet, createFileRoute, redirect, useBlocker, useMatchRoute, useRouter} from '@tanstack/solid-router'
import {useQuery} from '@tanstack/solid-query'
import {Tabs, TooltipIconButton} from '@conciv/ui-kit-system'
import {ChevronDown, PictureInPicture2, Unplug} from 'lucide-solid'
import ChevronDown from 'lucide-solid/icons/chevron-down'
import PictureInPicture2 from 'lucide-solid/icons/picture-in-picture-2'
import Unplug from 'lucide-solid/icons/unplug'
import {For, Show, Suspense, createMemo, createSignal, type JSX} from 'solid-js'
import {Dynamic} from 'solid-js/web'
import {isSessionId} from '@conciv/protocol/chat-types'
Expand Down
5 changes: 4 additions & 1 deletion apps/conciv/src/routes/quick.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {useQuery} from '@tanstack/solid-query'
import {createHotkey} from '@tanstack/solid-hotkeys'
import {For, Show, Suspense, onCleanup, onMount, type JSX} from 'solid-js'
import {TooltipIconButton, createResizable} from '@conciv/ui-kit-system'
import {ChevronUp, Columns2, PictureInPicture2, X} from 'lucide-solid'
import ChevronUp from 'lucide-solid/icons/chevron-up'
import Columns2 from 'lucide-solid/icons/columns-2'
import PictureInPicture2 from 'lucide-solid/icons/picture-in-picture-2'
import X from 'lucide-solid/icons/x'
import {useAppData, useRpc, useSuppressed} from '../app/context.js'
import {PaneProvider} from '../app/pane-provider.js'
import {ChatPane} from '../pane/chat-pane.js'
Expand Down
2 changes: 1 addition & 1 deletion apps/conciv/src/shell/notices.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Show, type JSX} from 'solid-js'
import {X} from 'lucide-solid'
import X from 'lucide-solid/icons/x'
import {Button, Toast, ToastGroup, TooltipIconButton, createToaster} from '@conciv/ui-kit-system'

export type NoticeTone = 'info' | 'success' | 'warn' | 'danger'
Expand Down
2 changes: 0 additions & 2 deletions apps/conciv/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
"tsBuildInfoFile": ".tsbuildinfo",
"rootDir": ".",
"noEmit": true,
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2024", "DOM", "DOM.Iterable"],
"jsx": "preserve",
"jsxImportSource": "solid-js",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/cards/code-run-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Show, type JSX} from 'solid-js'
import {Code} from 'lucide-solid'
import Code from 'lucide-solid/icons/code'
import type {ToolCardProps} from '@conciv/protocol/tool-view-types'
import {Markdown} from '@conciv/ui-kit-chat'
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/extensions/recorder/src/client.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {JSX} from 'solid-js'
import {Clapperboard} from 'lucide-solid'
import Clapperboard from 'lucide-solid/icons/clapperboard'
import {defineExtension, type RegisterExtension} from '@conciv/extension'
import {RECORDER_NAME, recorderConfig} from './shared/protocol.js'
import {recordingAttachment} from './shared/attachment.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/extensions/recorder/src/tool/card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {For, Show, type JSX} from 'solid-js'
import {z} from 'zod'
import {Clapperboard} from 'lucide-solid'
import Clapperboard from 'lucide-solid/icons/clapperboard'
import type {ToolCardProps} from '@conciv/protocol/tool-view-types'
import {Chip, ErrorBlock, parseInput, resultText, ToolCard} from '@conciv/ui-kit-chat/tools'
const ImagePartSchema = z.object({type: z.literal('image')}).loose()
Expand Down
2 changes: 1 addition & 1 deletion packages/extensions/tanstack/src/client/inspector-chip.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {JSX} from 'solid-js'
import {Waypoints} from 'lucide-solid'
import Waypoints from 'lucide-solid/icons/waypoints'
import {Tooltip} from '@conciv/ui-kit-system'
import {CONCIV_TANSTACK_CLIENT_LABEL} from '../client-sentinel.js'

Expand Down
2 changes: 1 addition & 1 deletion packages/extensions/terminal/src/client.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {SquareTerminal} from 'lucide-solid'
import SquareTerminal from 'lucide-solid/icons/square-terminal'
import {defineExtension, type RegisterExtension} from '@conciv/extension'
import {TERMINAL_NAME} from './shared/protocol.js'
import {TerminalPanelView} from './client/terminal-panel-view.js'
Expand Down
5 changes: 4 additions & 1 deletion packages/extensions/terminal/src/client/terminal-actions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {createMemo, createResource, createSignal, For, Match, Show, Switch, type JSX} from 'solid-js'
import {Crosshair, Plus, RotateCw, SquareArrowOutUpRight} from 'lucide-solid'
import Crosshair from 'lucide-solid/icons/crosshair'
import Plus from 'lucide-solid/icons/plus'
import RotateCw from 'lucide-solid/icons/rotate-cw'
import SquareArrowOutUpRight from 'lucide-solid/icons/square-arrow-out-up-right'
import {ModelSelector, useModelSelectorContext} from '@conciv/ui-kit-chat'
import type {ModelOption} from '@conciv/ui-kit-chat'
import {Button, TooltipIconButton} from '@conciv/ui-kit-system'
Expand Down
3 changes: 2 additions & 1 deletion packages/extensions/test-runner/src/tool/card.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {createEffect, createMemo, For, onCleanup, Show, type JSX} from 'solid-js'
import {createStore} from 'solid-js/store'
import {StatusDot, type StatusDotTone} from '@conciv/ui-kit-system'
import {ExternalLink, Sparkles} from 'lucide-solid'
import ExternalLink from 'lucide-solid/icons/external-link'
import Sparkles from 'lucide-solid/icons/sparkles'
import {
ActionButton,
ActionRow,
Expand Down
4 changes: 3 additions & 1 deletion packages/extensions/try-it/src/client/connect-pane.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {getHostApi} from '@conciv/extension'
import {Collapsible, Tooltip, TooltipIconButton} from '@conciv/ui-kit-system'
import {Check, Copy, TriangleAlert} from 'lucide-solid'
import Check from 'lucide-solid/icons/check'
import Copy from 'lucide-solid/icons/copy'
import TriangleAlert from 'lucide-solid/icons/triangle-alert'
import {createEffect, createSignal, onMount, Show, type JSX} from 'solid-js'
import {makeTimer} from '@solid-primitives/timer'
import {useCoreProbe} from './use-core-probe.js'
Expand Down
3 changes: 2 additions & 1 deletion packages/extensions/whiteboard/src/client.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Show, createRoot, createSignal, type JSX} from 'solid-js'
import {MessageSquarePlus, Presentation} from 'lucide-solid'
import MessageSquarePlus from 'lucide-solid/icons/message-square-plus'
import Presentation from 'lucide-solid/icons/presentation'
import {defineExtension, getHostApi} from '@conciv/extension'
import {TooltipIconButton} from '@conciv/ui-kit-system'
import {WHITEBOARD_NAME, WHITEBOARD_PROMPT} from './shared/meta.js'
Expand Down
3 changes: 2 additions & 1 deletion packages/extensions/whiteboard/src/client/inbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {For, Show, createMemo, createSignal, type JSX} from 'solid-js'
import {z} from 'zod'
import {Inbox as InboxIcon, ListFilter} from 'lucide-solid'
import InboxIcon from 'lucide-solid/icons/inbox'
import ListFilter from 'lucide-solid/icons/list-filter'
import {Button, RelativeTime, ScrollArea, TextField, TooltipIconButton} from '@conciv/ui-kit-system'
import {useComments, type Comment} from './model/comments.js'
import {Avatar, Menu, MenuCheckboxItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, Tabs} from './ui.js'
Expand Down
3 changes: 2 additions & 1 deletion packages/extensions/whiteboard/src/tool/canvas/card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Show, type JSX} from 'solid-js'
import {z} from 'zod'
import {Palette, Trash2} from 'lucide-solid'
import Palette from 'lucide-solid/icons/palette'
import Trash2 from 'lucide-solid/icons/trash-2'
import type {ToolCardProps} from '@conciv/protocol/tool-view-types'
import {Chip, ErrorBlock, parseInput, parseResultMedia, ResultImage, ToolCard} from '@conciv/ui-kit-chat/tools'
import {failureOf} from '../card-util.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/extensions/whiteboard/src/tool/comment/card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Show, type JSX} from 'solid-js'
import {z} from 'zod'
import {MessageSquare} from 'lucide-solid'
import MessageSquare from 'lucide-solid/icons/message-square'
import type {ToolCardProps} from '@conciv/protocol/tool-view-types'
import {Chip, ErrorBlock, parseInput, parseResultMedia, ToolCard} from '@conciv/ui-kit-chat/tools'
import {failureOf} from '../card-util.js'
Expand Down
1 change: 1 addition & 0 deletions packages/oxlint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"devDependencies": {
"@conciv/vitest-config": "workspace:*",
"@types/node": "^22.19.21",
"lucide-solid": "^1.18.0",
"oxlint": "^1.69.0",
"typescript": "^6.0.3",
"vitest": "^4.1.8"
Expand Down
137 changes: 137 additions & 0 deletions packages/oxlint-plugin/src/lucide-per-icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {createRequire} from 'node:module'
import {dirname} from 'node:path'

const LUCIDE_MODULE = 'lucide-solid'

function kebabCase(name) {
return name
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([A-Za-z])([0-9])/g, '$1-$2')
.replace(/([0-9])([A-Za-z])/g, '$1-$2')
.toLowerCase()
}

const resolutionCache = new Map()

function resolvesOnDisk(filename, iconPath) {
const cacheKey = `${dirname(filename)}::${iconPath}`
const cached = resolutionCache.get(cacheKey)
if (cached !== undefined) return cached
const resolved = tryResolve(filename, iconPath)
resolutionCache.set(cacheKey, resolved)
return resolved
}

function tryResolve(filename, iconPath) {
try {
createRequire(filename).resolve(iconPath)
return true
} catch {
return false
}
}

function isTypeOnlyDeclaration(node) {
return node.importKind === 'type'
}

function isTypeOnlySpecifier(specifier) {
return specifier.importKind === 'type'
}

function isImportSpecifier(specifier) {
return specifier.type === 'ImportSpecifier'
}

function importedName(specifier) {
return specifier.imported.type === 'Identifier' ? specifier.imported.name : specifier.imported.value
}

function specifierText(specifier) {
const imported = importedName(specifier)
const local = specifier.local.name
return imported === local ? imported : `${imported} as ${local}`
}

function splitSpecifiers(node) {
const valueSpecifiers = []
const typeSpecifiers = []
for (const specifier of node.specifiers) {
if (!isImportSpecifier(specifier)) continue
if (isTypeOnlySpecifier(specifier)) typeSpecifiers.push(specifier)
else valueSpecifiers.push(specifier)
}
return {valueSpecifiers, typeSpecifiers}
}

function iconImportPath(specifier) {
return `${LUCIDE_MODULE}/icons/${kebabCase(importedName(specifier))}`
}

function unresolvedSpecifier(filename, valueSpecifiers) {
return valueSpecifiers.find((specifier) => !resolvesOnDisk(filename, iconImportPath(specifier)))
}

function valueImportLines(valueSpecifiers) {
return valueSpecifiers.map((specifier) => `import ${specifier.local.name} from '${iconImportPath(specifier)}'`)
}

function typeImportLine(typeSpecifiers) {
if (typeSpecifiers.length === 0) return []
const names = typeSpecifiers.map(specifierText).join(', ')
return [`import type {${names}} from '${LUCIDE_MODULE}'`]
}

function buildFix(node, valueSpecifiers, typeSpecifiers) {
return (fixer) => {
const lines = [...valueImportLines(valueSpecifiers), ...typeImportLine(typeSpecifiers)]
return fixer.replaceText(node, lines.join('\n'))
}
}

function reportResolved(node, context, valueSpecifiers, typeSpecifiers) {
context.report({node, messageId: 'barrelImport', fix: buildFix(node, valueSpecifiers, typeSpecifiers)})
}

function reportUnresolved(node, context, blocker) {
context.report({
node,
messageId: 'barrelImportUnresolved',
data: {name: importedName(blocker), guess: iconImportPath(blocker)},
})
}

function reportBlockedOrResolved(node, context, valueSpecifiers, typeSpecifiers) {
const blocker = unresolvedSpecifier(context.filename, valueSpecifiers)
if (blocker === undefined) reportResolved(node, context, valueSpecifiers, typeSpecifiers)
else reportUnresolved(node, context, blocker)
}

function reportBarrelImport(node, context) {
if (node.source.value !== LUCIDE_MODULE) return
if (isTypeOnlyDeclaration(node)) return
const {valueSpecifiers, typeSpecifiers} = splitSpecifiers(node)
if (valueSpecifiers.length === 0) return
reportBlockedOrResolved(node, context, valueSpecifiers, typeSpecifiers)
}

export default {
meta: {
type: 'problem',
fixable: 'code',
messages: {
barrelImport:
"Import each icon from its own entry point (e.g. import ArrowUp from 'lucide-solid/icons/arrow-up') instead of the 'lucide-solid' barrel: it pulls every icon module through the transform on cold start.",
barrelImportUnresolved:
"Import each icon from its own entry point instead of the 'lucide-solid' barrel, but '{{name}}' has no resolvable '{{guess}}' entry point (likely a legacy alias for a renamed icon): import it manually from its real per-icon path.",
},
schema: [],
},
createOnce(context) {
return {
ImportDeclaration(node) {
reportBarrelImport(node, context)
},
}
},
}
2 changes: 2 additions & 0 deletions packages/oxlint-plugin/src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import bannedVocabulary from './banned-vocabulary.js'
import corePurity from './core-purity.js'
import lucidePerIcon from './lucide-per-icon.js'
import noComments from './no-comments.js'
import noLocatorPoll from './no-locator-poll.js'
import noPredicateWaits from './no-predicate-waits.js'
Expand All @@ -13,6 +14,7 @@ export default {
rules: {
'banned-vocabulary': bannedVocabulary,
'core-purity': corePurity,
'lucide-per-icon': lucidePerIcon,
'no-comments': noComments,
'no-locator-poll': noLocatorPoll,
'no-predicate-waits': noPredicateWaits,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ArrowUp from 'lucide-solid/icons/arrow-up'

function icon() {
return ArrowUp
}

export {icon}
Loading
Loading