mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-10 08:07:03 +00:00
* feat(ai-chat): link workspace paths and show tool item references Detect Windmill paths (u/..., f/...) in assistant messages and render them as clickable pills with the right icon, resolved against a per- workspace cache. Tool execution headers now list the script/flow/app paths referenced in tool parameters as external links. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ai-chat): linkify inline-code paths, refine pill styling - Inline-code spans whose value is exactly a Windmill path now render as a link pill (paths inside larger inline code or fenced blocks stay as code). - Tool-header chips moved to their own row to avoid overflow clipping when the title wraps. - Borderless pills, no default background (hover only), kind icons use the home-page palette (script blue, flow teal, app orange), and the external-link indicator only appears on hover. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ai-chat): linkify variables/resources/triggers + inline drawer - Workspace item registry now also lists variables, resources, schedules, and all 10 trigger kinds; resource wins over variable on path collisions (Windmill auto-creates a companion variable for every resource). - Pill icons delegated to the canonical RowIcon component so each kind matches the home-page styling (script blue, flow teal, app orange, resource boxes, schedule calendar, etc.). - Pill href includes the hash fragment each list page already consumes (#/resource/<path>, #<path> for variables/schedules/triggers), so opening the link puts the user on the list page with the matching editor drawer already open. - For variable and resource pills, a hover-revealed side-panel button opens (or toggles closed) the editor drawer inline next to the chat, without navigating away. VariableEditor and ResourceEditorDrawer gain a closeDrawer() export and forward their close event so the host can drive toggling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: simplify ai chat workspace item links * refactor: keep ai chat path linkification only * perf: avoid eager ai chat path cache loads * refactor: simplify ai chat path linking * feat: open ai chat path links in drawers * refactor: homogenize workspace item kinds * fix: toggle ai chat item drawer * refactor: trim ai chat path cache * fix: cancel ai chat drawer reopen --------- Co-authored-by: Guilhem Lemouel <guilhemlemouel@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import type { OffboardAffectedPaths } from '$lib/gen'
|
|
|
|
export function pl(n: number, singular: string): string {
|
|
return `${n} ${singular}${n === 1 ? '' : 's'}`
|
|
}
|
|
|
|
function triggerCount(triggers: OffboardAffectedPaths['triggers']): number {
|
|
if (!triggers) return 0
|
|
return Object.values(triggers).reduce((s, arr) => s + arr.length, 0)
|
|
}
|
|
|
|
export function countPaths(p: OffboardAffectedPaths | undefined | null): number {
|
|
if (!p) return 0
|
|
return (
|
|
(p.scripts?.length ?? 0) +
|
|
(p.flows?.length ?? 0) +
|
|
(p.apps?.length ?? 0) +
|
|
(p.resources?.length ?? 0) +
|
|
(p.variables?.length ?? 0) +
|
|
(p.schedules?.length ?? 0) +
|
|
triggerCount(p.triggers)
|
|
)
|
|
}
|
|
|
|
const TRIGGER_TABLE_TO_ROUTE: Record<string, string> = {
|
|
http_trigger: 'routes',
|
|
websocket_trigger: 'websocket_triggers',
|
|
kafka_trigger: 'kafka_triggers',
|
|
postgres_trigger: 'postgres_triggers',
|
|
mqtt_trigger: 'mqtt_triggers',
|
|
nats_trigger: 'nats_triggers',
|
|
sqs_trigger: 'sqs_triggers',
|
|
gcp_trigger: 'gcp_triggers',
|
|
azure_trigger: 'azure_triggers',
|
|
email_trigger: 'email_triggers'
|
|
}
|
|
|
|
const TRIGGER_TABLE_TO_LABEL: Record<string, string> = {
|
|
http_trigger: 'http trigger',
|
|
websocket_trigger: 'websocket trigger',
|
|
kafka_trigger: 'kafka trigger',
|
|
postgres_trigger: 'postgres trigger',
|
|
mqtt_trigger: 'mqtt trigger',
|
|
nats_trigger: 'nats trigger',
|
|
sqs_trigger: 'sqs trigger',
|
|
gcp_trigger: 'gcp trigger',
|
|
azure_trigger: 'azure trigger',
|
|
email_trigger: 'email trigger'
|
|
}
|
|
|
|
export function flattenPaths(
|
|
p: OffboardAffectedPaths | undefined | null
|
|
): Array<{ kind: string; path: string }> {
|
|
if (!p) return []
|
|
const result: Array<{ kind: string; path: string }> = []
|
|
for (const [kind, list] of Object.entries(p)) {
|
|
if (kind === 'triggers' && list && typeof list === 'object' && !Array.isArray(list)) {
|
|
for (const [triggerType, paths] of Object.entries(list as Record<string, string[]>)) {
|
|
for (const path of paths) result.push({ kind: triggerType, path })
|
|
}
|
|
} else if (Array.isArray(list)) {
|
|
for (const path of list) result.push({ kind, path })
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
export function triggerLabel(triggerType: string): string {
|
|
return TRIGGER_TABLE_TO_LABEL[triggerType] ?? triggerType
|
|
}
|
|
|
|
export function kindLabel(kind: string): string {
|
|
if (TRIGGER_TABLE_TO_LABEL[kind]) return TRIGGER_TABLE_TO_LABEL[kind]
|
|
// "scripts" -> "script", "flows" -> "flow", etc.
|
|
return kind.replace(/s$/, '')
|
|
}
|
|
|
|
export function itemHref(kind: string, path: string): string | undefined {
|
|
switch (kind) {
|
|
case 'script':
|
|
case 'scripts':
|
|
return `/scripts/get/${path}`
|
|
case 'flow':
|
|
case 'flows':
|
|
return `/flows/get/${path}`
|
|
case 'app':
|
|
case 'apps':
|
|
return `/apps/get/${path}`
|
|
case 'resource':
|
|
case 'resources':
|
|
return `/resources#/resource/${path}`
|
|
case 'variable':
|
|
case 'variables':
|
|
return `/variables#${path}`
|
|
case 'schedule':
|
|
case 'schedules':
|
|
return `/schedules#${path}`
|
|
default: {
|
|
const route = TRIGGER_TABLE_TO_ROUTE[kind]
|
|
if (route) return `/${route}#${path}`
|
|
return undefined
|
|
}
|
|
}
|
|
}
|
|
|
|
export function downloadCsv(rows: string[][], filename: string) {
|
|
const csv = rows.map((r) => r.map((c) => `"${c.replace(/"/g, '""')}"`).join(',')).join('\n')
|
|
const blob = new Blob([csv], { type: 'text/csv' })
|
|
const url = URL.createObjectURL(blob)
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = filename
|
|
a.click()
|
|
URL.revokeObjectURL(url)
|
|
}
|