SLOPSHOPPER

pulse

Sidebar file tree that opens files in their default app and pulses a Claude mark on files edited in the latest turn

newpaneguardcommandtoastprocess
v0.2.0no licenseupdated 2026-10-09its-coughfee/pulse-file-tree
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · pulse
│ ┃ File tree ✕ › fix the failing auth test and add an audit log call │ ┃ ▾ app │ ┃ package.json ⏺ Read(src/auth.ts) │ ┃ src ⎿ Read 6 lines │ ┃ README.md ⏺ Update(src/auth.ts) │ ⎿ Added 2 lines, removed 1 line │ ⏺ Bash(bun test) │ ⎿ 3 pass, 1 fail │ │ ● Done. refresh now rejects expired claims and logs an audit event. │ │ ✻ Worked for 42s · done 4:20 PM │ │ › /files │ ⎿ pulse: File tree opened. │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · File tree
▾ app package.json src README.md
README

Pulse – file tree for Claude

A Claude Code mod that adds a file tree to the sidebar, built around two ideas: you can see at a glance which files Claude is editing, and clicking a file opens it in the app you'd normally use for it.

What makes it different

A pulsing mark shows what Claude is editing

When Claude edits a file, the Claude mark appears next to that file in the tree. While Claude is still working on the turn, the mark gently pulses, fading in and out, so you can watch edits land as they happen. When the turn ends, the pulse stops and the mark holds still, so you can still see which files changed.

When the next turn starts editing, the old marks clear and the new turn's files get marked instead. You only ever see the latest turn's changes, never a build-up from earlier ones.

If an edited file is inside a closed folder, the mark shows on the folder instead, so nothing is hidden. The mark sits in its own fixed space at the start of each row, so rows never shift when it appears or disappears.

The mark appears for anything Claude changes with its Edit, Write, MultiEdit or NotebookEdit tools.

Files open in their default app

Clicking a file doesn't open it inside Claude Code. It opens in whatever app Windows uses for that file type: a Markdown note in Obsidian, a spreadsheet in Excel, a PDF in your PDF reader, an image in your photo viewer. It's the same as double-clicking the file in File Explorer. If the file can't be opened, a short message says so.

Each file shows an icon for its type from Material Icon Theme, the same icons many people know from VS Code, so you can tell file types apart without reading the names. Around 45 common types have their own icon, from HTML, JavaScript and Python to Word, PDF, spreadsheets, images, audio and video. Any other type gets a plain page icon.

Using it

The tree opens in the sidebar when a session starts. If you close it, type /files to bring it back.

  • Click a folder to open or close it.
  • Click a file to open it in its default app.
  • .git, node_modules, .obsidian and __pycache__ folders are hidden.

Install

In Claude Code, run:

/plugin marketplace add its-coughfee/pulse-file-tree
/plugin install pulse@alex-plugins

Requirements

Windows only. Opening files relies on File Explorer.

Credits

File and folder icons are from Material Icon Theme by Material Extensions, used under the MIT licence (see icons/LICENSE).

Source 3 files
hooks/register.tsx 236 lines
1import { update } from 'claude-code'
2import type { FsEntry, Hook, Register, RenderChildren } from 'claude-code'
3import { BY_EXTENSION, BY_NAME, ICONS } from './icons'
4
5// the engine interface every hook receives as $
6type Engine = Parameters<Hook<'ui.focus'>>[0]
7
8const PANE = 'pulse'
9const MARK = '✻'
10// the real Claude starburst, near-white on transparent, 48px (source: assets/claude-mark.png)
11const MARK_PNG =
12  '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'
13const SKIP = new Set(['.git', 'node_modules', '.obsidian', '__pycache__'])
14const EDIT_TOOLS = ['Edit', 'Write', 'MultiEdit', 'NotebookEdit']
15
16// Windows paths compare case-blind and with either slash
17const norm = (path: string) => path.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase()
18const join = (dir: string, name: string) => `${dir.replace(/[\\/]+$/, '')}/${name}`
19
20const byFoldersFirst = (a: FsEntry, b: FsEntry) =>
21  a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind === 'dir' ? -1 : 1
22
23// the Claude mark as a drawing; while a turn runs it fades in and out smoothly
24const markSvg = (isPulsing: boolean) => `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 48 48">
25  <g>${
26    isPulsing
27      ? '<animate attributeName="opacity" values="1;0.25;1" dur="1.6s" repeatCount="indefinite" calcMode="spline" keySplines="0.45 0 0.55 1;0.45 0 0.55 1"/>'
28      : ''
29  }
30    <image width="48" height="48" href="data:image/png;base64,${MARK_PNG}" xlink:href="data:image/png;base64,${MARK_PNG}"/>
31  </g>
32</svg>`
33
34// each file type's Material Icon Theme icon (see icons/), a plain page for any other
35const iconSvg = (entry: FsEntry) => {
36  const name = entry.name.toLowerCase()
37  const ext = name.includes('.') ? name.split('.').pop()! : ''
38  const kind = entry.kind === 'dir' ? 'folder' : (BY_NAME[name] ?? BY_EXTENSION[ext] ?? 'file')
39
40  return ICONS[kind] ?? ICONS['file']!
41}
42
43// one click can arrive as both a focus and a press, in either order; the row acts on
44// whichever comes first and skips the other, so the two don't cancel each other out.
45// Two presses in a row both act, however quick
46const DEDUPE_MS = 300
47type Via = 'focus' | 'press'
48let lastAct = { element: '', at: 0, via: 'press' as Via }
49const actOnce = (via: Via, element: string, act: () => Promise<void>) => {
50  const isTwin = lastAct.element === element && lastAct.via !== via && Date.now() - lastAct.at < DEDUPE_MS
51  // a skipped twin clears the record, so the next signal always acts
52  lastAct = isTwin ? { element: '', at: 0, via } : { element, at: Date.now(), via }
53  if (!isTwin) void act()
54}
55
56// flips the folder against what is stored now, not what a drawing saw
57const toggle = async ($: Engine, key: string) => {
58  await update($, { plugin: 'pulse', key: 'expanded' } as const, (list = []) =>
59    list.includes(key) ? list.filter(one => one !== key) : [...list, key],
60  )
61}
62
63// the session's own folder starts open, so its stored flag is the other way round
64const toggleRoot = async ($: Engine) => {
65  await update($, { plugin: 'pulse', key: 'isRootClosed' } as const, (isClosed = false) => !isClosed)
66}
67
68// Explorer opens a file in its default app; says so when it can't instead of doing nothing
69// (explorer's exit code is 1 even on success, so only a failure to start counts)
70const openFile = async ($: Engine, full: string) => {
71  const path = full.replace(/\//g, '\\')
72  try {
73    await $.process.run(['explorer.exe', path])
74  } catch (error) {
75    $.ui.toast(`Couldn't open ${path}: ${String(error)}`)
76  }
77}
78
79export const register: Register = on => {
80  on('session.start', async ($, e, next) => {
81    await $.command.register({ name: 'files', description: 'Show the file tree in the sidebar' })
82    void $.ui.open({ id: PANE, title: 'File tree' })
83
84    return next(e)
85  })
86
87  on('command.run', { command: 'files' }, async $ => {
88    await $.ui.open({ id: PANE, title: 'File tree' })
89
90    return { text: 'File tree opened.' }
91  })
92
93  on('turn.start', async ($, e, next) => {
94    await $.state.set({ plugin: 'pulse', key: 'isFreshTurn' } as const, true)
95
96    return next(e)
97  })
98
99  // the pulse runs from the first edit until the turn ends, then the mark holds still
100  on('turn.complete', async ($, e, next) => {
101    await $.state.set({ plugin: 'pulse', key: 'isWorking' } as const, false)
102
103    return next(e)
104  })
105
106  on('tool.call', async ($, e, next) => {
107    const ran = await next(e)
108    if (!EDIT_TOOLS.includes(e.tool) || ran.deny !== undefined) return ran
109    const input = e as { file_path?: string; notebook_path?: string }
110    const path = input.file_path ?? input.notebook_path
111    if (path === undefined) return ran
112    const { value: isFirst = false } = await $.state.get({ plugin: 'pulse', key: 'isFreshTurn' } as const)
113    const { value: marks = [] } = await $.state.get({ plugin: 'pulse', key: 'edited' } as const)
114    await $.state.set({ plugin: 'pulse', key: 'isFreshTurn' } as const, false)
115    await $.state.set({ plugin: 'pulse', key: 'isWorking' } as const, true)
116    await $.state.set({ plugin: 'pulse', key: 'edited' } as const, [
117      ...new Set([...(isFirst ? [] : marks), norm(path)]),
118    ])
119
120    return ran
121  })
122
123  // A click on the pane while it isn't active only focuses the row and raises no press,
124  // so a person's focus landing on a row acts as the click; the press that may follow is skipped
125  on('ui.focus', { requestId: PANE }, async ($, e, next) => {
126    const result = await next(e)
127    const element = e.element
128    if (e.origin.kind !== 'person' || element === undefined) return result
129    if (element === 'root') actOnce('focus', element, () => toggleRoot($))
130    else if (element.startsWith('dir:')) actOnce('focus', element, () => toggle($, element.slice(4)))
131    else if (element.startsWith('file:')) actOnce('focus', element, () => openFile($, element.slice(5)))
132
133    return result
134  })
135
136  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
137    const { Box, Text, Button } = $.ui.resolve(e)
138    // only the desktop, editor and phone draw pictures; the terminal keeps the text mark
139    const Svg = e.surface === 'terminal' ? undefined : $.ui.resolve({ ...e, surface: 'desktop' }).Svg
140    const canDraw = Svg !== undefined
141    const root = await $.session.cwd()
142    const { value: openList = [] } = await $.state.get({ plugin: 'pulse', key: 'expanded' } as const)
143    const { value: marks = [] } = await $.state.get({ plugin: 'pulse', key: 'edited' } as const)
144    const { value: isWorking = false } = await $.state.get({ plugin: 'pulse', key: 'isWorking' } as const)
145    const open = new Set(openList)
146
147    const pressed = (element: string, act: () => Promise<void>) => actOnce('press', element, act)
148
149    const mark = (isMarked: boolean) => {
150      if (!isMarked && !canDraw) return <Text>{'  '}</Text>
151      // the mark sits in a slot of fixed width, there or not, so it never moves the row beside it
152      if (canDraw) {
153        return (
154          <Box flexDirection="row" width={3} minWidth={3} flexShrink={0}>
155            {isMarked ? <Svg source={markSvg(isWorking)} alt="Edited by Claude" width={14} height={14} /> : <Text>{' '}</Text>}
156          </Box>
157        )
158      }
159
160      return <Text color="claude">{MARK} </Text>
161    }
162
163    const icon = (entry: FsEntry) =>
164      canDraw ? (
165        <Box flexDirection="row" flexShrink={0}>
166          <Text> </Text>
167          <Svg source={iconSvg(entry)} alt={entry.kind === 'dir' ? 'Folder' : 'File'} width={16} height={16} />
168        </Box>
169      ) : null
170
171    const rows: RenderChildren[] = []
172    const walk = async (dir: string, depth: number) => {
173      let entries: FsEntry[] = []
174      try {
175        entries = await $.fs.list(dir)
176      } catch {
177        return
178      }
179      for (const entry of entries.filter(one => !SKIP.has(one.name)).sort(byFoldersFirst)) {
180        const full = join(dir, entry.name)
181        const key = norm(full)
182        // the indent is padding and the name the only part allowed to give way, so a long
183        // name gets cut short at the edge instead of squeezing its row out of line
184        if (entry.kind === 'dir') {
185          const isOpen = open.has(key)
186          const hasEdits = !isOpen && marks.some(one => one.startsWith(`${key}/`))
187          rows.push(
188            <Box flexDirection="row" paddingLeft={depth * 2}>
189              {mark(hasEdits)}
190              <Box flexShrink={1} minWidth={0} overflow="hidden">
191                <Button key={`dir:${key}`} plain onPress={() => pressed(`dir:${key}`, () => toggle($, key))}>
192                  {`${isOpen ? '▾' : '▸'} ${entry.name}`}
193                </Button>
194              </Box>
195              {icon(entry)}
196            </Box>,
197          )
198          if (isOpen) await walk(full, depth + 1)
199        } else {
200          rows.push(
201            <Box flexDirection="row" paddingLeft={depth * 2}>
202              {mark(marks.includes(key))}
203              <Box flexShrink={1} minWidth={0} overflow="hidden">
204                <Button key={`file:${key}`} plain dimColor onPress={() => pressed(`file:${key}`, () => openFile($, full))}>
205                  {`  ${entry.name}`}
206                </Button>
207              </Box>
208              {icon(entry)}
209            </Box>,
210          )
211        }
212      }
213    }
214    const { value: isRootClosed = false } = await $.state.get({ plugin: 'pulse', key: 'isRootClosed' } as const)
215    if (!isRootClosed) await walk(root, 1)
216
217    // the session's own folder heads the tree, open until a person closes it
218    const rootName = root.replace(/[\\/]+$/, '').split(/[\\/]/).pop() || root
219    const rootEntry: FsEntry = { name: rootName, kind: 'dir', size: 0, mtimeMs: 0, isLink: false }
220
221    return (
222      <Box flexDirection="column">
223        <Box flexDirection="row">
224          {mark(isRootClosed && marks.some(one => one.startsWith(`${norm(root)}/`)))}
225          <Button key="root" plain onPress={() => pressed('root', () => toggleRoot($))}>
226            {`${isRootClosed ? '▸' : '▾'} ${rootName}`}
227          </Button>
228          {icon(rootEntry)}
229        </Box>
230        {!isRootClosed && rows.length === 0 && <Text dimColor>{'    This folder is empty.'}</Text>}
231        {rows}
232      </Box>
233    )
234  })
235}
236
hooks/icons.ts 154 lines
1// GENERATED from the SVGs in /icons, which come from Material Icon Theme
2// (https://github.com/material-extensions/vscode-material-icon-theme, MIT, see icons/LICENSE).
3// The module can only import code, so each drawing is kept here as text.
4
5export const ICONS: Record<string, string> = {
6  "audio": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ef5350\" d=\"M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m6 10h-4v8a4 4 0 1 1-4-4 3.96 3.96 0 0 1 2 .555V8h6Z\"/></svg>",
7  "c": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#0288d1\" d=\"M19.563 22A5.57 5.57 0 0 1 14 16.437v-2.873A5.57 5.57 0 0 1 19.563 8H24V2h-4.437A11.563 11.563 0 0 0 8 13.563v2.873A11.564 11.564 0 0 0 19.563 28H24v-6Z\"/></svg>",
8  "certificate": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#ff5722\" d=\"M2 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h6v3l2-1.25L12 14v-3h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1Zm0 1h4v1H2Zm6 0 2 1.25L12 3v2.5l2 1-2 1V10l-2-1.25L8 10V7.5l-2-1 2-1zM2 5h3v1H2Zm0 2h3v1H2Zm0 2h4v1H2Z\"/></svg>",
9  "console": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#ff7043\" d=\"M2 2a1 1 0 0 0-1 1v10c0 .554.446 1 1 1h12c.554 0 1-.446 1-1V3a1 1 0 0 0-1-1zm0 3h12v8H2zm1 2 2 2-2 2 1 1 3-3-3-3zm5 3.5V12h5v-1.5z\"/></svg>",
10  "cpp": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#0288d1\" d=\"M28 14v-4h-2v4h-6v-4h-2v4h-4v2h4v4h2v-4h6v4h2v-4h4v-2z\"/><path fill=\"#0288d1\" d=\"M13.563 22A5.57 5.57 0 0 1 8 16.437v-2.873A5.57 5.57 0 0 1 13.563 8H18V2h-4.437A11.563 11.563 0 0 0 2 13.563v2.873A11.564 11.564 0 0 0 13.563 28H18v-6Z\"/></svg>",
11  "csharp": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#0288d1\" d=\"M30 14v-2h-2V8h-2v4h-2V8h-2v4h-2v2h2v2h-2v2h2v4h2v-4h2v4h2v-4h2v-2h-2v-2Zm-4 2h-2v-2h2Zm-12.437 6A5.57 5.57 0 0 1 8 16.437v-2.873A5.57 5.57 0 0 1 13.563 8H18V2h-4.437A11.563 11.563 0 0 0 2 13.563v2.873A11.564 11.564 0 0 0 13.563 28H18v-6Z\"/></svg>",
12  "css": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#7e57c2\" d=\"M20 18h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 20 22h2v2h2v-2c0-.388-.562-.851-1.254-1.034C20.356 20.34 20 18.84 20 18m-3.254 2.966C14.356 20.34 14 18.84 14 18h-2v-2h-2v8h2v-2h4v2h2v-2c0-.388-.562-.851-1.254-1.034\"/><path fill=\"#7e57c2\" d=\"M24 4H4v20a4 4 0 0 0 4 4h16.16A3.84 3.84 0 0 0 28 24.16V8a4 4 0 0 0-4-4m2 14h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 26 22v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2Z\"/></svg>",
13  "database": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ffca28\" d=\"M16 24c-5.525 0-10-.9-10-2v4c0 1.1 4.475 2 10 2s10-.9 10-2v-4c0 1.1-4.475 2-10 2m0-8c-5.525 0-10-.9-10-2v4c0 1.1 4.475 2 10 2s10-.9 10-2v-4c0 1.1-4.475 2-10 2m0-12C10.477 4 6 4.895 6 6v4c0 1.1 4.475 2 10 2s10-.9 10-2V6c0-1.105-4.477-2-10-2\"/></svg>",
14  "document": "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/><path fill=\"#42a5f5\" d=\"M8 16h8v2H8zm0-4h8v2H8zm6-10H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8zm4 18H6V4h7v5h5z\"/></svg>",
15  "exe": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#e64a19\" d=\"M28 4H4a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 22H4V10h24Z\"/></svg>",
16  "font": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#f44336\" d=\"M24 28h4L18 4h-4L4 28h4l8-19.422\"/><path fill=\"#f44336\" d=\"M8 20h16v4H8z\"/></svg>",
17  "git": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#e64a19\" d=\"M13.172 2.828 11.78 4.22l1.91 1.91 2 2A2.986 2.986 0 0 1 20 10.81a3.25 3.25 0 0 1-.31 1.31l2.06 2a2.68 2.68 0 0 1 3.37.57 2.86 2.86 0 0 1 .88 2.117 3.02 3.02 0 0 1-.856 2.109A2.9 2.9 0 0 1 23 19.81a2.93 2.93 0 0 1-2.13-.87 2.694 2.694 0 0 1-.56-3.38l-2-2.06a3 3 0 0 1-.31.12V20a3 3 0 0 1 1.44 1.09 2.92 2.92 0 0 1 .56 1.72 2.88 2.88 0 0 1-.878 2.128 2.98 2.98 0 0 1-2.048.871 2.981 2.981 0 0 1-2.514-4.719A3 3 0 0 1 16 20v-6.38a2.96 2.96 0 0 1-1.44-1.09 2.9 2.9 0 0 1-.56-1.72 2.9 2.9 0 0 1 .31-1.31l-3.9-3.9-7.579 7.572a4 4 0 0 0-.001 5.658l10.342 10.342a4 4 0 0 0 5.656 0l10.344-10.344a4 4 0 0 0 0-5.656L18.828 2.828a4 4 0 0 0-5.656 0\"/></svg>",
18  "go": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#00acc1\" d=\"M2 12h4v2H2zm-2 4h6v2H0zm4 4h2v2H4zm16.954-5H14v3h3.239a4.42 4.42 0 0 1-3.531 2 2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 15.292 13a2.73 2.73 0 0 1 1.749.584l2.962-1.185A5.6 5.6 0 0 0 15.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 6.4 6.4 0 0 0 .003-1.5\"/><path fill=\"#00acc1\" d=\"M26.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 5.614 5.614 0 0 0-5.659-6.5m2.681 6.137A4.515 4.515 0 0 1 24.708 20a2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 26.292 13a2.65 2.65 0 0 1 2.053.858 2.86 2.86 0 0 1 .628 2.28Z\"/></svg>",
19  "html": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#e65100\" d=\"m4 4 2 22 10 2 10-2 2-22Zm19.72 7H11.28l.29 3h11.86l-.802 9.335L15.99 25l-6.635-1.646L8.93 19h3.02l.19 2 3.86.77 3.84-.77.29-4H8.84L8 8h16Z\"/></svg>",
20  "image": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#26a69a\" d=\"M8.5 6h4l-4-4zM3.875 1H9.5l4 4v8.6c0 .773-.616 1.4-1.375 1.4h-8.25c-.76 0-1.375-.627-1.375-1.4V2.4c0-.777.612-1.4 1.375-1.4M4 13.6h8V8l-2.625 2.8L8 9.4zm1.25-7.7c-.76 0-1.375.627-1.375 1.4s.616 1.4 1.375 1.4c.76 0 1.375-.627 1.375-1.4S6.009 5.9 5.25 5.9\"/></svg>",
21  "java": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#f44336\" d=\"M4 26h24v2H4zM28 4H7a1 1 0 0 0-1 1v13a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-4h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 8h-4V6h4Z\"/></svg>",
22  "javascript": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#ffca28\" d=\"M2 2v12h12V2zm6 6h1v4a1.003 1.003 0 0 1-1 1H7a1.003 1.003 0 0 1-1-1v-1h1v1h1zm3 0h2v1h-2v1h1a1.003 1.003 0 0 1 1 1v1a1.003 1.003 0 0 1-1 1h-2v-1h2v-1h-1a1.003 1.003 0 0 1-1-1V9a1.003 1.003 0 0 1 1-1\"/></svg>",
23  "json": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\"><path fill=\"#f9a825\" d=\"M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80z\"/></svg>",
24  "key": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#26a69a\" d=\"M30 14H17.738a8 8 0 1 0 0 4H24v4h4v-4h2Zm-20 5a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3\"/></svg>",
25  "lock": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ffd54f\" d=\"M25 12h-3V8a6 6 0 0 0-12 0v4H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1M14 8a2 2 0 0 1 4 0v4h-4Zm2 17a4 4 0 1 1 4-4 4 4 0 0 1-4 4\"/></svg>",
26  "log": "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/><path fill=\"#afb42b\" d=\"M19 5v9h-5v5H5V5zm0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h10l6-6V5c0-1.1-.9-2-2-2m-7 11H7v-2h5zm5-4H7V8h10z\"/></svg>",
27  "lua": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#42a5f5\" d=\"M30 6a3.86 3.86 0 0 1-1.167 2.833 4.024 4.024 0 0 1-5.666 0A3.86 3.86 0 0 1 22 6a3.86 3.86 0 0 1 1.167-2.833 4.024 4.024 0 0 1 5.666 0A3.86 3.86 0 0 1 30 6m-9.208 5.208A10.6 10.6 0 0 0 13 8a10.6 10.6 0 0 0-7.792 3.208A10.6 10.6 0 0 0 2 19a10.6 10.6 0 0 0 3.208 7.792A10.6 10.6 0 0 0 13 30a10.6 10.6 0 0 0 7.792-3.208A10.6 10.6 0 0 0 24 19a10.6 10.6 0 0 0-3.208-7.792m-1.959 7.625a4.024 4.024 0 0 1-5.666 0 4.024 4.024 0 0 1 0-5.666 4.024 4.024 0 0 1 5.666 0 4.024 4.024 0 0 1 0 5.666\"/></svg>",
28  "markdown": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#42a5f5\" d=\"m14 10-4 3.5L6 10H4v12h4v-6l2 2 2-2v6h4V10zm12 6v-6h-4v6h-4l6 8 6-8z\"/></svg>",
29  "pdf": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ef5350\" d=\"M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m4.93 10.44c.41.9.93 1.64 1.53 2.15l.41.32c-.87.16-2.07.44-3.34.93l-.11.04.5-1.04c.45-.87.78-1.66 1.01-2.4m6.48 3.81c.18-.18.27-.41.28-.66.03-.2-.02-.39-.12-.55-.29-.47-1.04-.69-2.28-.69l-1.29.07-.87-.58c-.63-.52-1.2-1.43-1.6-2.56l.04-.14c.33-1.33.64-2.94-.02-3.6a.85.85 0 0 0-.61-.24h-.24c-.37 0-.7.39-.79.77-.37 1.33-.15 2.06.22 3.27v.01c-.25.88-.57 1.9-1.08 2.93l-.96 1.8-.89.49c-1.2.75-1.77 1.59-1.88 2.12-.04.19-.02.36.05.54l.03.05.48.31.44.11c.81 0 1.73-.95 2.97-3.07l.18-.07c1.03-.33 2.31-.56 4.03-.75 1.03.51 2.24.74 3 .74.44 0 .74-.11.91-.3m-.41-.71.09.11c-.01.1-.04.11-.09.13h-.04l-.19.02c-.46 0-1.17-.19-1.9-.51.09-.1.13-.1.23-.1 1.4 0 1.8.25 1.9.35M7.83 17c-.65 1.19-1.24 1.85-1.69 2 .05-.38.5-1.04 1.21-1.69zm3.02-6.91c-.23-.9-.24-1.63-.07-2.05l.07-.12.15.05c.17.24.19.56.09 1.1l-.03.16-.16.82z\"/></svg>",
30  "php": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#1e88e5\" d=\"M12 18.08c-6.63 0-12-2.72-12-6.08s5.37-6.08 12-6.08S24 8.64 24 12s-5.37 6.08-12 6.08m-5.19-7.95c.54 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.58 1.09q-.42.33-1.29.33h-.87l.53-2.76zm-3.5 5.55h1.44l.34-1.75h1.23c.54 0 .98-.06 1.33-.17.35-.12.67-.31.96-.58.24-.22.43-.46.58-.73.15-.26.26-.56.31-.88.16-.78.05-1.39-.33-1.82-.39-.44-.99-.65-1.82-.65H4.59zm7.25-8.33-1.28 6.58h1.42l.74-3.77h1.14c.36 0 .6.06.71.18s.13.34.07.66l-.57 2.93h1.45l.59-3.07c.13-.62.03-1.07-.27-1.36-.3-.27-.85-.4-1.65-.4h-1.27L12 7.35zM18 10.13c.55 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.57 1.09-.29.22-.72.33-1.3.33h-.85l.5-2.76zm-3.5 5.55h1.44l.34-1.75h1.22c.55 0 1-.06 1.35-.17.35-.12.65-.31.95-.58.24-.22.44-.46.58-.73.15-.26.26-.56.32-.88.15-.78.04-1.39-.34-1.82-.36-.44-.99-.65-1.82-.65h-2.75z\"/></svg>",
31  "powerpoint": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#e64a19\" d=\"M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zM8 11v2h1v6H8v1h4v-1h-1v-2h2a3 3 0 0 0 3-3 3 3 0 0 0-3-3zm5 2a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-2v-2z\"/></svg>",
32  "powershell": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#03a9f4\" d=\"M29.07 6H7.677A1.535 1.535 0 0 0 6.24 7.113l-4.2 17.774A.852.852 0 0 0 2.93 26h21.393a1.535 1.535 0 0 0 1.436-1.113L29.96 7.112A.852.852 0 0 0 29.07 6M8.626 23.797a1.4 1.4 0 0 1-1.814-.31l-.007-.009a1.075 1.075 0 0 1 .315-1.599l9.6-6.061-6.102-5.852-.01-.01a1.068 1.068 0 0 1 .084-1.625l.037-.03a1.38 1.38 0 0 1 1.8.07l7.233 6.957a1.1 1.1 0 0 1 .236.739 1.08 1.08 0 0 1-.412.79c-.074.04-.146.119-10.951 6.935ZM24 22.94A1.135 1.135 0 0 1 22.803 24h-5.634a1.061 1.061 0 1 1 .001-2.112h5.633A1.134 1.134 0 0 1 24 22.938Z\"/></svg>",
33  "python": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#0288d1\" d=\"M9.86 2A2.86 2.86 0 0 0 7 4.86v1.68h4.29c.39 0 .71.57.71.96H4.86A2.86 2.86 0 0 0 2 10.36v3.781a2.86 2.86 0 0 0 2.86 2.86h1.18v-2.68a2.85 2.85 0 0 1 2.85-2.86h5.25c1.58 0 2.86-1.271 2.86-2.851V4.86A2.86 2.86 0 0 0 14.14 2zm-.72 1.61c.4 0 .72.12.72.71s-.32.891-.72.891c-.39 0-.71-.3-.71-.89s.32-.711.71-.711\"/><path fill=\"#fdd835\" d=\"M17.959 7v2.68a2.85 2.85 0 0 1-2.85 2.859H9.86A2.85 2.85 0 0 0 7 15.389v3.75a2.86 2.86 0 0 0 2.86 2.86h4.28A2.86 2.86 0 0 0 17 19.14v-1.68h-4.291c-.39 0-.709-.57-.709-.96h7.14A2.86 2.86 0 0 0 22 13.64V9.86A2.86 2.86 0 0 0 19.14 7zM8.32 11.513l-.004.004.038-.004zm6.54 7.276c.39 0 .71.3.71.89a.71.71 0 0 1-.71.71c-.4 0-.72-.12-.72-.71s.32-.89.72-.89\"/></svg>",
34  "react": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#00bcd4\" d=\"M16 12c7.444 0 12 2.59 12 4s-4.556 4-12 4-12-2.59-12-4 4.556-4 12-4m0-2c-7.732 0-14 2.686-14 6s6.268 6 14 6 14-2.686 14-6-6.268-6-14-6\"/><path fill=\"#00bcd4\" d=\"M16 14a2 2 0 1 0 2 2 2 2 0 0 0-2-2\"/><path fill=\"#00bcd4\" d=\"M10.458 5.507c2.017 0 5.937 3.177 9.006 8.493 3.722 6.447 3.757 11.687 2.536 12.392a.9.9 0 0 1-.457.1c-2.017 0-5.938-3.176-9.007-8.492C8.814 11.553 8.779 6.313 10 5.608a.9.9 0 0 1 .458-.1m-.001-2A2.87 2.87 0 0 0 9 3.875C6.13 5.532 6.938 12.304 10.804 19c3.284 5.69 7.72 9.493 10.74 9.493A2.87 2.87 0 0 0 23 28.124c2.87-1.656 2.062-8.428-1.804-15.124-3.284-5.69-7.72-9.493-10.74-9.493Z\"/><path fill=\"#00bcd4\" d=\"M21.543 5.507a.9.9 0 0 1 .457.1c1.221.706 1.186 5.946-2.536 12.393-3.07 5.316-6.99 8.493-9.007 8.493a.9.9 0 0 1-.457-.1C8.779 25.686 8.814 20.446 12.536 14c3.07-5.316 6.99-8.493 9.007-8.493m0-2c-3.02 0-7.455 3.804-10.74 9.493C6.939 19.696 6.13 26.468 9 28.124a2.87 2.87 0 0 0 1.457.369c3.02 0 7.455-3.804 10.74-9.493C25.061 12.304 25.87 5.532 23 3.876a2.87 2.87 0 0 0-1.457-.369\"/></svg>",
35  "react_ts": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#0288d1\" d=\"M16 12c7.444 0 12 2.59 12 4s-4.556 4-12 4-12-2.59-12-4 4.556-4 12-4m0-2c-7.732 0-14 2.686-14 6s6.268 6 14 6 14-2.686 14-6-6.268-6-14-6\"/><path fill=\"#0288d1\" d=\"M16 14a2 2 0 1 0 2 2 2 2 0 0 0-2-2\"/><path fill=\"#0288d1\" d=\"M10.458 5.507c2.017 0 5.937 3.177 9.006 8.493 3.722 6.447 3.757 11.687 2.536 12.392a.9.9 0 0 1-.457.1c-2.017 0-5.938-3.176-9.007-8.492C8.814 11.553 8.779 6.313 10 5.608a.9.9 0 0 1 .458-.1m-.001-2A2.87 2.87 0 0 0 9 3.875C6.13 5.532 6.938 12.304 10.804 19c3.284 5.69 7.72 9.493 10.74 9.493A2.87 2.87 0 0 0 23 28.124c2.87-1.656 2.062-8.428-1.804-15.124-3.284-5.69-7.72-9.493-10.74-9.493Z\"/><path fill=\"#0288d1\" d=\"M21.543 5.507a.9.9 0 0 1 .457.1c1.221.706 1.186 5.946-2.536 12.393-3.07 5.316-6.99 8.493-9.007 8.493a.9.9 0 0 1-.457-.1C8.779 25.686 8.814 20.446 12.536 14c3.07-5.316 6.99-8.493 9.007-8.493m0-2c-3.02 0-7.455 3.804-10.74 9.493C6.939 19.696 6.13 26.468 9 28.124a2.87 2.87 0 0 0 1.457.369c3.02 0 7.455-3.804 10.74-9.493C25.061 12.304 25.87 5.532 23 3.876a2.87 2.87 0 0 0-1.457-.369\"/></svg>",
36  "ruby": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#f44336\" d=\"M18.041 3.177c2.24.382 2.879 1.919 2.843 3.527V6.67l-1.013 13.266-13.132.897h.008c-1.093-.044-3.518-.151-3.634-3.545l1.217-2.222 2.462 5.74 2.097-6.77-.045.009.018-.018 6.85 2.186L13.945 9.3l6.53-.409-5.144-4.212 2.71-1.51v.009M3.113 17.252v.017zM6.916 6.874c2.63-2.622 6.033-4.168 7.34-2.844 1.297 1.306-.072 4.523-2.702 7.135-2.666 2.613-6.015 4.248-7.322 2.933-1.306-1.324.036-4.612 2.675-7.224z\"/></svg>",
37  "rust": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ff7043\" d=\"m30 12-4-2V6h-4l-2-4-4 2-4-2-2 4H6v4l-4 2 2 4-2 4 4 2v4h4l2 4 4-2 4 2 2-4h4v-4l4-2-2-4ZM6 16a9.9 9.9 0 0 1 .842-4H10v8H6.842A9.9 9.9 0 0 1 6 16m10 10a9.98 9.98 0 0 1-7.978-4H16v-2h-2v-2h4c.819.819.297 2.308 1.179 3.37a1.89 1.89 0 0 0 1.46.63h3.34A9.98 9.98 0 0 1 16 26m-2-12v-2h4a1 1 0 0 1 0 2Zm11.158 6H24a2.006 2.006 0 0 1-2-2 2 2 0 0 0-2-2 3 3 0 0 0 3-3q0-.08-.004-.161A3.115 3.115 0 0 0 19.83 10H8.022a9.986 9.986 0 0 1 17.136 10\"/></svg>",
38  "sass": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ec407a\" d=\"M27.837 5.673a4.33 4.33 0 0 0-2.293-2.701c-2.362-1.261-6.11-1.298-9.548-.092a26.3 26.3 0 0 0-8.76 4.966c-2.752 2.542-3.438 4.925-3.189 6.194.523 2.668 3.274 4.539 5.485 6.042.418.284.822.559 1.175.816-1.429.76-4.261 2.444-5.088 4.248a3.88 3.88 0 0 0-.118 3.332A2.37 2.37 0 0 0 6.869 29.8a5.6 5.6 0 0 0 1.49.2 6.35 6.35 0 0 0 5.19-2.856 6.74 6.74 0 0 0 .864-5.382 7.3 7.3 0 0 1 2.044-.03 3.92 3.92 0 0 1 2.816 1.311 1.82 1.82 0 0 1 .423 1.262 1.55 1.55 0 0 1-.772 1.05c-.234.14-.586.355-.504.803.036.194.198.633.894.512a2.93 2.93 0 0 0 2.145-2.651 4 4 0 0 0-1.197-2.904 5.94 5.94 0 0 0-4.396-1.626 10.6 10.6 0 0 0-2.672.304 20 20 0 0 0-2.203-1.846c-1.712-1.3-3.33-2.529-3.235-4.26.125-2.263 2.468-4.532 6.964-6.744 4.016-1.976 7.254-2.037 8.944-1.438a2 2 0 0 1 1.204.883 2.77 2.77 0 0 1-.36 2.47 9.71 9.71 0 0 1-7.425 4.304 3.86 3.86 0 0 1-3.238-.757c-.278-.302-.593-.645-1.074-.383q-.565.31-.225 1.189a3.9 3.9 0 0 0 2.407 1.92 11.7 11.7 0 0 0 7.128-.671c3.527-1.35 6.681-5.202 5.756-8.787M11.895 24.475a4 4 0 0 1-.192.468 4.5 4.5 0 0 1-.753 1.081 2.83 2.83 0 0 1-2.533 1.107c-.056-.032-.078-.146-.085-.193a3.28 3.28 0 0 1 1.076-2.284 11.3 11.3 0 0 1 2.644-1.933 3.85 3.85 0 0 1-.157 1.754\"/></svg>",
39  "settings": "<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/><path fill=\"#42a5f5\" d=\"M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.49.49 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.6.6 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1q.09.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64zm-1.98-1.71c.04.31.05.52.05.73s-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4m0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2\"/></svg>",
40  "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ffb300\" d=\"M29.168 14.03a2.7 2.7 0 0 0-1.968-.83 2.51 2.51 0 0 0-1.929.8h-4.443l3.078-3.078a2.835 2.835 0 0 0 2.857-2.842 2.6 2.6 0 0 0-.831-1.969 2.82 2.82 0 0 0-2.014-.788 2.67 2.67 0 0 0-1.968.788 2.36 2.36 0 0 0-.812 1.922L18 11.17V6.726a2.51 2.51 0 0 0 .8-1.929 2.7 2.7 0 0 0-.832-1.968 2.745 2.745 0 0 0-3.936 0 2.7 2.7 0 0 0-.832 1.968 2.51 2.51 0 0 0 .8 1.93v4.443l-3.138-3.138a2.36 2.36 0 0 0-.812-1.922 2.66 2.66 0 0 0-1.968-.788 2.83 2.83 0 0 0-2.014.788 2.6 2.6 0 0 0-.831 1.969 2.74 2.74 0 0 0 .831 2.013 2.8 2.8 0 0 0 2.026.829l3.078 3.078H6.729a2.51 2.51 0 0 0-1.929-.8 2.7 2.7 0 0 0-1.968.831 2.745 2.745 0 0 0 0 3.937 2.7 2.7 0 0 0 1.968.832 2.51 2.51 0 0 0 1.929-.8h4.443l-3.078 3.077a2.835 2.835 0 0 0-2.857 2.842 2.6 2.6 0 0 0 .831 1.969 2.82 2.82 0 0 0 2.014.788 2.67 2.67 0 0 0 1.968-.788 2.36 2.36 0 0 0 .812-1.922L14 20.827v4.444a2.51 2.51 0 0 0-.8 1.929 2.784 2.784 0 0 0 4.768 1.968A2.7 2.7 0 0 0 18.8 27.2a2.51 2.51 0 0 0-.8-1.929v-4.444l3.138 3.138a2.36 2.36 0 0 0 .812 1.922 2.66 2.66 0 0 0 1.968.788 2.83 2.83 0 0 0 2.014-.788 2.6 2.6 0 0 0 .831-1.969 2.74 2.74 0 0 0-.831-2.013 2.8 2.8 0 0 0-2.026-.829L20.828 18h4.443a2.51 2.51 0 0 0 1.93.8 2.784 2.784 0 0 0 1.967-4.769Z\"/></svg>",
41  "table": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#8bc34a\" d=\"M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zm4 7.5h-4v2h1l-2 1.67L10 13h1v-2H7v2h1l3 2.5L8 18H7v2h4v-2h-1l2-1.67L14 18h-1v2h4v-2h-1l-3-2.5 3-2.5h1z\"/></svg>",
42  "tex": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\"><path fill=\"#2196f3\" d=\"M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z\"/></svg>",
43  "toml": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#cfd8dc\" d=\"M4 6V4h8v2H9v7H7V6z\"/><path fill=\"#ef5350\" d=\"M4 1v1H2v12h2v1H1V1zm8 0v1h2v12h-2v1h3V1z\"/></svg>",
44  "typescript": "<svg xmlns=\"http://www.w3.org/2000/svg\" xml:space=\"preserve\" viewBox=\"0 0 16 16\"><path fill=\"#0288d1\" d=\"M2 2v12h12V2zm4 6h3v1H8v4H7V9H6zm5 0h2v1h-2v1h1a1.003 1.003 0 0 1 1 1v1a1.003 1.003 0 0 1-1 1h-2v-1h2v-1h-1a1.003 1.003 0 0 1-1-1V9a1.003 1.003 0 0 1 1-1\"/></svg>",
45  "video": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><path fill=\"#ff9800\" d=\"m24 6 2 6h-4l-2-6h-3l2 6h-4l-2-6h-3l2 6H8L6 6H5a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h22a3 3 0 0 0 3-3V6Z\"/></svg>",
46  "word": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#01579b\" d=\"M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zM7 13l1.5 7h2l1.5-3 1.5 3h2l1.5-7h1v-2h-4v2h1l-.9 4.2L13 15h-2l-1.1 2.2L9 13h1v-2H6v2z\"/></svg>",
47  "xml": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#8bc34a\" d=\"M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m.12 13.5 3.74 3.74 1.42-1.41-2.33-2.33 2.33-2.33-1.42-1.41zm11.16 0-3.74-3.74-1.42 1.41 2.33 2.33-2.33 2.33 1.42 1.41z\"/></svg>",
48  "yaml": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ff5252\" d=\"M13 9h5.5L13 3.5zM6 2h8l6 6v12c0 1.1-.9 2-2 2H6c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2m12 16v-2H9v2zm-4-4v-2H6v2z\"/></svg>",
49  "zip": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#afb42b\" d=\"M14 17h-2v-2h-2v-2h2v2h2m0-6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2m5-4H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2\"/></svg>",
50  "file": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#90a4ae\" d=\"M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2\"/></svg>",
51  "folder": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"#90a4ae\" d=\"m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232\"/></svg>"
52}
53
54export const BY_EXTENSION: Record<string, string> = {
55  "html": "html",
56  "htm": "html",
57  "js": "javascript",
58  "mjs": "javascript",
59  "cjs": "javascript",
60  "ts": "typescript",
61  "mts": "typescript",
62  "cts": "typescript",
63  "jsx": "react",
64  "tsx": "react_ts",
65  "py": "python",
66  "json": "json",
67  "jsonc": "json",
68  "css": "css",
69  "scss": "sass",
70  "sass": "sass",
71  "md": "markdown",
72  "markdown": "markdown",
73  "txt": "document",
74  "rtf": "document",
75  "pdf": "pdf",
76  "png": "image",
77  "jpg": "image",
78  "jpeg": "image",
79  "gif": "image",
80  "webp": "image",
81  "bmp": "image",
82  "ico": "image",
83  "svg": "svg",
84  "yaml": "yaml",
85  "yml": "yaml",
86  "toml": "toml",
87  "doc": "word",
88  "docx": "word",
89  "csv": "table",
90  "tsv": "table",
91  "xls": "table",
92  "xlsx": "table",
93  "ppt": "powerpoint",
94  "pptx": "powerpoint",
95  "xml": "xml",
96  "sh": "console",
97  "bash": "console",
98  "bat": "console",
99  "cmd": "console",
100  "ps1": "powershell",
101  "psm1": "powershell",
102  "zip": "zip",
103  "7z": "zip",
104  "rar": "zip",
105  "gz": "zip",
106  "tar": "zip",
107  "mp3": "audio",
108  "wav": "audio",
109  "m4a": "audio",
110  "flac": "audio",
111  "ogg": "audio",
112  "mp4": "video",
113  "mov": "video",
114  "mkv": "video",
115  "webm": "video",
116  "avi": "video",
117  "ini": "settings",
118  "cfg": "settings",
119  "conf": "settings",
120  "env": "settings",
121  "lock": "lock",
122  "lua": "lua",
123  "rs": "rust",
124  "go": "go",
125  "java": "java",
126  "c": "c",
127  "h": "c",
128  "cpp": "cpp",
129  "hpp": "cpp",
130  "cs": "csharp",
131  "php": "php",
132  "rb": "ruby",
133  "db": "database",
134  "sqlite": "database",
135  "sql": "database",
136  "ttf": "font",
137  "otf": "font",
138  "woff": "font",
139  "woff2": "font",
140  "log": "log",
141  "key": "key",
142  "pem": "certificate",
143  "crt": "certificate",
144  "exe": "exe",
145  "msi": "exe",
146  "tex": "tex"
147}
148
149export const BY_NAME: Record<string, string> = {
150  ".gitignore": "git",
151  ".gitattributes": "git",
152  "license": "certificate"
153}
154
types/index.d.ts 19 lines
1declare module 'claude-code' {
2  interface PluginState {
3    pulse: {
4      // folders the person has expanded, normalised paths
5      expanded: string[]
6      // files Claude edited in the current (or, until it edits, the last) turn
7      edited: string[]
8      // true on the first edit of a turn: that edit replaces the old marks
9      isFreshTurn: boolean
10      // true from Claude's first edit until the turn ends: the mark pulses
11      isWorking: boolean
12      // true once a person closes the session's own folder, which starts open
13      isRootClosed: boolean
14    }
15  }
16}
17
18export {}
19