Keep Tailwind's scan off the server-side folders

The task work added ref strings ([file:ID], [bot:3]) in new prompt and tool
files, so the warning about "file" not being a known CSS property came back:
Tailwind read them as arbitrary-property utilities and emitted `file: ID`.

The exclusion list now covers every server-side directory whole instead of
one file at a time (none of them holds a className, so nothing is lost), and
`dict` stays scanned for the static hue classes. The ref literals that must
live in scanned files are reworded in comments, and task-file-row builds its
chip through `fileRef` rather than its own template literal.
This commit is contained in:
2026-10-22 06:06:25 +01:00
parent 7cb26c4e36
commit 07042a2288
5 changed files with 36 additions and 19 deletions
+5 -3
View File
@@ -3,6 +3,7 @@ import Link from "@moduletrace/twui/layout/Link";
import { FileText, Image as ImageIcon } from "lucide-react"; import { FileText, Image as ImageIcon } from "lucide-react";
import Span from "@moduletrace/twui/layout/Span"; import Span from "@moduletrace/twui/layout/Span";
import type { TaskFile } from "@/src/types"; import type { TaskFile } from "@/src/types";
import fileRef from "@/src/utils/file-ref";
type Props = { type Props = {
file: TaskFile; file: TaskFile;
@@ -15,8 +16,9 @@ type Props = {
/** /**
* One file on a task: its name, opening the bytes at * One file on a task: its name, opening the bytes at
* `/media/private/<id>`, and its `[file:ID]` chip when it lives in a bot's * `/media/private/<id>`, and its file ref (built by `fileRef`) when it
* or workspace's folder (so the file's real home is reachable too). * lives in a bot's or workspace's folder (so the file's real home is
* reachable too).
*/ */
export default function TaskFileRow({ file, onDetach, busy }: Props) { export default function TaskFileRow({ file, onDetach, busy }: Props) {
if (file.missing) { if (file.missing) {
@@ -51,7 +53,7 @@ export default function TaskFileRow({ file, onDetach, busy }: Props) {
) : null} ) : null}
{file.bot_file_id ? ( {file.bot_file_id ? (
<Span className="shrink-0 text-[11px] leading-4 text-ink-secondary"> <Span className="shrink-0 text-[11px] leading-4 text-ink-secondary">
{`[file:${file.bot_file_id}]`} {fileRef({ id: file.bot_file_id })}
</Span> </Span>
) : null} ) : null}
{onDetach ? ( {onDetach ? (
@@ -1,6 +1,6 @@
/** /**
* What the composer can mention, by the character that starts it: `@` a * What the composer can mention, by the character that starts it: `@` a
* bot (`[bot:ID]`), `#` a workspace (`[workspace:ID]`). * bot, `#` a workspace (each written as its ref, `[bot:` + id).
*/ */
export const ComposerMentionKinds = { export const ComposerMentionKinds = {
"@": "bot", "@": "bot",
@@ -43,8 +43,8 @@ type Params = {
/** /**
* The composer's editor (Tiptap): markdown in and out, with bold, italic, * The composer's editor (Tiptap): markdown in and out, with bold, italic,
* strike, code, lists, quotes and links (no headings), and mentions of * strike, code, lists, quotes and links (no headings), and mentions of
* bots (`@`) and workspaces (`#`) written as `[bot:ID]` and * bots (`@`) and workspaces (`#`), written as their bot or workspace ref.
* `[workspace:ID]`. Enter sends and Shift+Enter breaks the line, except * Enter sends and Shift+Enter breaks the line, except
* in a code block or list, where Enter is the editor's and Mod+Enter * in a code block or list, where Enter is the editor's and Mod+Enter
* sends. Enter picks from an open mention menu. Pasted text comes in * sends. Enter picks from an open mention menu. Pasted text comes in
* plain, without another site's styling; pasted or dropped files go to * plain, without another site's styling; pasted or dropped files go to
@@ -8,10 +8,10 @@ import {
const RefPattern = /^\[(bot|workspace):(\d+)\](?!\()/; const RefPattern = /^\[(bot|workspace):(\d+)\](?!\()/;
/** /**
* How the composer's mention node reads and writes markdown: a bot as * How the composer's mention node reads and writes markdown: a bot ref
* `[bot:3]` (`botRef`), a workspace as `[workspace:2]` (`workspaceRef`), * (`botRef`) or a workspace ref (`workspaceRef`), so a draft or a sent
* so a draft or a sent message holds the same refs bots write. A ref * message holds the same refs bots write. A ref followed by `(` is a
* followed by `(` is a markdown link, not a mention. * markdown link, not a mention.
*/ */
export const MentionMarkdown = { export const MentionMarkdown = {
markdownTokenizer: { markdownTokenizer: {
@@ -50,7 +50,7 @@ export const MentionMarkdown = {
}; };
/** /**
* The ref a mention writes, `[bot:3]` unless it's a workspace's. * The ref a mention writes, a bot's unless it's a workspace's.
*/ */
export function mentionRef({ id, char }: { id?: string | number | null; char?: string | null }) { export function mentionRef({ id, char }: { id?: string | number | null; char?: string | null }) {
const kind = ComposerMentionKinds[(char || "@") as ComposerMentionChar] || "bot"; const kind = ComposerMentionKinds[(char || "@") as ComposerMentionChar] || "bot";
+23 -8
View File
@@ -1,17 +1,32 @@
@import "@moduletrace/twui/base.css"; @import "@moduletrace/twui/base.css";
/* Doc and prompt strings mention refs like `[file:12]`, which Tailwind would /* Doc, prompt and tool strings mention refs like `[file:12]`, which Tailwind
read as an arbitrary-property utility and emit as `file: 12` (esbuild then reads as an arbitrary-property utility and emits as `file: 12` (esbuild then
warns it is not a CSS property). None of these files holds a className. */ warns it is not a CSS property). None of the server-side directories below
holds a className, so they are excluded from the scan whole; `dict` stays
scanned because it carries the static `bg-hue-*` class names. A new prompt
or tool file then cannot bring the warning back. */
@source not "../../AGENTS.md"; @source not "../../AGENTS.md";
@source not "../../db/**"; @source not "../../db/**";
@source not "../../docs/**";
@source not "../../assets/**";
@source not "../../llm-code-assistant/**";
@source not "../../packages/**";
@source not "../**/*.test.ts"; @source not "../**/*.test.ts";
@source not "../**/*.test.tsx"; @source not "../**/*.test.tsx";
@source not "../data/harness-prompts.ts"; @source not "../bots-queue/**";
@source not "../utils/file-ref.ts"; @source not "../cron/**";
@source not "../utils/link-computer-refs.ts"; @source not "../data/**";
@source not "../harness/tools/storage/list-files-tool.ts"; @source not "../file-ingest/**";
@source not "../harness/tools/storage/storage-tool-context.ts"; @source not "../functions/**";
@source not "../harness/**";
@source not "../hooks/**";
@source not "../live-relay/**";
@source not "../schemas/**";
@source not "../scripts/**";
@source not "../styles/**";
@source not "../utils/**";
@source not "../websocket/**";
/* Cobble design tokens (handoff `tokens/tokens.css`). Light on `:root`, dark /* Cobble design tokens (handoff `tokens/tokens.css`). Light on `:root`, dark
on `.dark` (twui's dark variant). Exposed to Tailwind in `@theme inline`. */ on `.dark` (twui's dark variant). Exposed to Tailwind in `@theme inline`. */