You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(timeline): lever l'ambiguïté quand deux clips partagent le même média
Un trim était stocké en temps source PAR ASSET, sans identité de clip. Dès que
deux clips tirent du même média (clip dupliqué, même enregistrement posé deux
fois), ils partagent le même espace de coordonnées : « sur quel clip est cette
coupe ? » n'avait pas de réponse, et chaque lecteur en inventait une. Un seul
défaut, trois symptômes :
- transcript : la coupe apparaissait sur LES DEUX clips ;
- règle : la pastille était dessinée sur le PREMIER ;
- lecture/export : la plage était retirée des DEUX clips.
AxcutTrimRange gagne `clipId` — `startSec`/`endSec` SONT déjà la fenêtre source,
donc c'était la seule pièce manquante pour rejoindre l'ancre de clip v5 que
zoom/annotation/speed ont depuis longtemps. `trimAppliesToClip` est désormais la
définition unique par laquelle passent buildClipSection, trimToTimelineSpan et
resolvePlaybackSegments. Un trim sans `clipId` garde le sens historique par
asset : les documents existants se lisent exactement comme avant.
Schéma 6 -> 7. La migration VENTILE au lieu de choisir : un trim stocké devient
une ligne ancrée par clip couvert, chacune bornée à la fenêtre de son clip. Un
trim v6 coupait réellement tous les clips de son asset, donc le rendu ne bouge
pas — mais les lignes sont maintenant adressables séparément, ce qui permet de
supprimer la copie posée sur le clip qu'on ne visait pas. Un trim ne couvrant
aucun clip est conservé tel quel plutôt que supprimé.
Corollaires du modèle : `duplicateClip` copie explicitement les trims ancrés sur
la copie (avant c'était un effet de bord du match par asset), et `removeClip`
supprime ceux du clip effacé pour qu'un jumeau du même asset ne les ressuscite
pas.
--- Transcript : la même ambiguïté un cran plus haut
Un transcript appartient à l'ASSET, donc deux clips sur un média projettent le
même AxcutWord deux fois : `word.id` nomme un instant dans le média, pas un
élément à l'écran. `ClipWord.id` = `clipWordId(clip.id, word.id)` devient
l'identité à l'écran (clé React, data-word-id, surlignage du mot lu, ancre du
curseur), et `findCueWordId` choisit sa section par `cue.clipId`. Les jetons
[silence] rendent ce cloisonnement inconditionnel : withSilenceGaps les numérote
à partir de 1 dans chaque clip, donc `silence_1` existait dans tous les blocs.
--- Deux défauts adjacents, trouvés en vérifiant
Cliquer un mot du transcript appelait `onSeek(word.startSec)` — du temps SOURCE —
alors que handleSeek attend du temps TIMELINE. Invisible tant qu'un seul clip
siégeait à la position 0, où les deux coïncident ; le surlignage désormais
cloisonné le rendait manifeste.
Backspace/Delete devenaient un no-op silencieux dès qu'un mot déjà coupé se
trouvait sur leur trajet : la résolution tombait dessus et skipWordRange le
jetait comme non conservé. La marche saute maintenant les mots déjà coupés, dans
les deux sens ; une garde qui prétendait traiter le cas renvoyait le mot qu'elle
venait d'écarter et était morte de toute façon.
--- Suppression d'un trim = toute la pastille
Une coupe étirée par-dessus une frontière de clip est nécessairement 2+ lignes
(ventilateTimelineSpanToTrims) rendues comme une seule pastille. La suppression
filtrait par id, laissant la moitié continuer de couper. `dropTrimPillsByIds`
aligne les trois chemins (touche Suppr, inspecteur, outil LLM removeTrim) sur ce
que dropPillById fait déjà pour les autres types ; le contournement local de
FloatingInspector disparaît.
Tests : aucun test existant ne couvrait deux clips partageant un asset avec des
fenêtres source qui se chevauchent — le garde-fou le plus proche prenait des
fenêtres à 45 s d'écart, donc il passait malgré le défaut. Chaque nouveau test a
été vérifié en échec sans son correctif.
"Read the recorded pointer track for an asset: where the cursor was over time, downsampled to a readable rate. Each point carries atSec (the asset's own source clock), virtualSec (the same instant on the edited timeline — the coordinate addZoom takes, null when no clip carries it), cx/cy as 0–1 fractions of the frame, and `shape`, an index into the pointer bitmaps the recording used (equal values are the same pointer; a change means the pointer changed, e.g. arrow to text caret). Points that are not plain moves carry `kind`; points a trim cuts out of playback carry `trimmed`. These are real samples, not a summary — reading what the pointer was doing is yours. Omit assetId for the primary asset. It answers `available:false` in two DIFFERENT ways you must not confuse: reason 'no-sidecar' means this asset was checked and genuinely has no telemetry, while reason 'unavailable' means it could not be read from here.",
144
144
addTrim:
145
-
"Add a trim range: a cut of a span inside a clip (this source-time span will not be played or exported) that does NOT split the clip. Times are in seconds of the asset's source time. This is the preferred (and for 'remove silences' requests, the only) way to handle silences; it preserves the user's placed clips and only adds a cut. Call this once per silent range.",
146
-
setTrim: "Move or resize an existing trim range by id. Times are source-time seconds.",
145
+
"Add a trim range: a cut of a span inside a clip (this source-time span will not be played or exported) that does NOT split the clip. Times are in seconds of the asset's source time. This is the preferred (and for 'remove silences' requests, the only) way to handle silences; it preserves the user's placed clips and only adds a cut. Call this once per silent range. A cut belongs to ONE clip: `clipId` is inferred when a single clip covers the range, but when several clips draw on the same asset over it the call FAILS and lists them — pass the `clipId` you mean (ids come from getCurrentDocument).",
146
+
setTrim:
147
+
"Move or resize an existing trim range by id. Times are source-time seconds. The cut follows to whichever clip the new range lands in, when that clip is unambiguous.",
147
148
setClipRange:
148
149
"Set a clip's in/out points (source-time seconds) to shorten its head or tail — distinct from a trim (which cuts a span inside the clip). All clips are re-laid back-to-back afterwards, so downstream clips shift automatically. Use this ONLY when the user explicitly asks to shorten or extend a user-placed clip. Do NOT use this for 'remove silences' or 'cut pauses' — for those, use addTrim.",
0 commit comments