Prototype de pipeline-v2 : sonde ce que chaque surface de Claude Code affiche (panneau, bande, ligne d'état, toast, notification).

hooks/register.tsx 122 lines1// Sonde de pipeline-v2 : montre chaque surface de Claude Code et note ce que le moteur en fait.
2//
3// Prototype jetable, sans gestion d'erreurs (Signal de dérive S3) : il sert à répondre à la
4// question qui décide du Chantier, ce que Claude Code suivi au navigateur affiche d'un mod.
5// La commande `/sonde` ouvre un panneau, pose une ligne d'état, un toast et une notification ;
6// une bande au-dessus de l'invite est dessinée dès le démarrage.
7//
8// Le journal, `journal.log` à la racine du mod, dit ce que le moteur a fait : chaque client qui
9// s'attache, chaque demande de dessin par surface, le placement du panneau, le sort de la
10// notification. L'utilisateur dit ce qui s'est vu ; les deux font le constat.
11
12import type { EngineInterface, Register } from 'claude-code'
13
14const PANNEAU = 'sonde'
15
16// `$.fs.write` réécrit le fichier entier : le journal se tient ici et se réécrit à chaque ligne.
17const lignes: string[] = []
18
19// Une demande de dessin revient à chaque rafraîchissement : seule une combinaison nouvelle
20// de composant, surface et taille mérite une ligne.
21const dessins = new Set<string>()
22
23const noter = async ($: EngineInterface, texte: string) => {
24 lignes.push(`${new Date(await $.clock.now()).toISOString()} ${texte}`)
25 await $.fs.write(`${$.plugin.root}/journal.log`, `${lignes.join('\n')}\n`)
26}
27
28const noterDessin = async (
29 $: EngineInterface,
30 composant: string,
31 surface: string,
32 viewport: unknown,
33) => {
34 const cle = `${composant} ${surface} ${JSON.stringify(viewport ?? null)}`
35 if (dessins.has(cle)) {
36 return
37 }
38 dessins.add(cle)
39 await noter($, `ui.render ${cle}`)
40}
41
42export const register: Register = on => {
43 on('session.start', async ($, e, next) => {
44 await $.command.register({
45 name: 'sonde',
46 description: 'pipeline-v2 : ouvre un panneau, pose ligne d’état, toast et notification',
47 })
48 await noter($, `session.start surfaces=${JSON.stringify(await $.session.surfaces())}`)
49
50 return next(e)
51 })
52
53 on('session.attach', async ($, e, next) => {
54 await noter(
55 $,
56 `session.attach surface=${e.surface} clientId=${e.clientId} viewport=${JSON.stringify(e.viewport ?? null)}`,
57 )
58
59 return next(e)
60 })
61
62 on('command.run', { command: 'sonde' }, async $ => {
63 await noter($, `/sonde surfaces=${JSON.stringify(await $.session.surfaces())}`)
64 const ouvert = await $.ui.open({ id: PANNEAU, title: 'Sonde pipeline-v2' })
65 await noter($, `ui.open ${JSON.stringify(ouvert)}`)
66 $.ui.status('pv2-sonde : ligne d’état')
67 $.ui.toast('pv2-sonde : toast', { timeoutMs: 15000 })
68 const notifie = await $.ui.notify('pv2-sonde : notification', { title: 'pipeline-v2' })
69 await noter($, `ui.notify ${JSON.stringify(notifie)}`)
70
71 return { text: 'Sonde lancée : panneau, ligne d’état, toast, notification.' }
72 })
73
74 on('ui.render', { component: 'Pane', requestId: PANNEAU }, async ($, e) => {
75 await noterDessin($, 'Pane', e.surface, e.viewport)
76 const { Box, Button, Markdown } = $.ui.resolve(e)
77
78 return (
79 <Box flexDirection="column">
80 <Markdown
81 text={[
82 `**Panneau de la sonde**, surface \`${e.surface}\`, placement \`${e.props.placement}\`.`,
83 '',
84 '- un lien [https](https://code.claude.com/docs)',
85 `- un lien [file](file://${$.plugin.root}/../README.md)`,
86 ].join('\n')}
87 />
88 <Button
89 key="appui"
90 label="Appuyer"
91 onPress={async () => {
92 await noter($, `ui.press panneau surface=${e.surface}`)
93 $.ui.toast(`pv2-sonde : bouton du panneau pressé (${e.surface})`)
94 }}
95 />
96 </Box>
97 )
98 })
99
100 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
101 await noterDessin($, 'AbovePrompt', e.surface, e.viewport)
102 if (e.props.hasSurvey) {
103 return next(e)
104 }
105 const { Box, Button, Text } = $.ui.resolve(e)
106
107 return (
108 <Box>
109 <Text>pv2-sonde : bande au-dessus de l’invite ({e.surface}) </Text>
110 <Button
111 key="bande"
112 label="Appuyer"
113 onPress={async () => {
114 await noter($, `ui.press bande surface=${e.surface}`)
115 $.ui.toast(`pv2-sonde : bouton de la bande pressé (${e.surface})`)
116 }}
117 />
118 </Box>
119 )
120 })
121}
122