dsh-as-aistudio 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-as-aistudio",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Google-AI-Studio-style turn controls for DeepSeek Harness, in one install: edit a prompt and re-run it, re-run a reply in place, delete a turn from the model context, and render sent prompts as Markdown. Self-contained: the plugin list shows this one row; the four components are dependencies mounted by it. Each still installs alone and works alone.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -48,7 +48,7 @@
48
48
  "verify:manifest": "node tools/verify-manifest.mjs"
49
49
  },
50
50
  "dependencies": {
51
- "dsh-delete-turn": "^0.1.7",
51
+ "dsh-delete-turn": "^0.1.8",
52
52
  "dsh-edit-turn": "^0.2.16",
53
53
  "dsh-markdown-bubble": "^0.1.4",
54
54
  "dsh-rerun-turn": "^0.1.24"
package/src/client.js CHANGED
@@ -3131,6 +3131,8 @@ window.__ModuleLoader__.load({
3131
3131
  this.animateOnce = false
3132
3132
  this.view = Object.freeze({
3133
3133
  hidden: new Map(),
3134
+ // replacement seq -> the seq it was recorded as shadowing. See hiddenViaOf.
3135
+ hiddenVia: new Map(),
3134
3136
  surface: new Set(),
3135
3137
  replyTurns: new Set(),
3136
3138
  edits: new Map(),
@@ -3195,6 +3197,7 @@ window.__ModuleLoader__.load({
3195
3197
  for (const item of Array.isArray(data.hidden) ? data.hidden : []) {
3196
3198
  if (item && typeof item.seq === 'number') hidden.set(item.seq, typeof item.mode === 'string' ? item.mode : 'message')
3197
3199
  }
3200
+ const hiddenVia = hiddenViaOf(data.hidden)
3198
3201
  const surface = new Set()
3199
3202
  for (const seq of Array.isArray(data.surface) ? data.surface : []) surface.add(seq)
3200
3203
  const replyTurns = new Set()
@@ -3208,7 +3211,7 @@ window.__ModuleLoader__.load({
3208
3211
  if (typeof turn === 'number') markerTurns.add(turn)
3209
3212
  }
3210
3213
  const surfaceThrough = typeof data.lastSeq === 'number' ? data.lastSeq : -1
3211
- this.publish({ hidden, surface, replyTurns, edits, markerTurns, surfaceReady: true, surfaceThrough, loaded: true, loadError: false })
3214
+ this.publish({ hidden, hiddenVia, surface, replyTurns, edits, markerTurns, surfaceReady: true, surfaceThrough, loaded: true, loadError: false })
3212
3215
  })
3213
3216
  .catch(() => {
3214
3217
  this.publish({ loadError: true })
@@ -3260,8 +3263,10 @@ window.__ModuleLoader__.load({
3260
3263
  for (const item of Array.isArray(data.hidden) ? data.hidden : []) {
3261
3264
  if (item && typeof item.seq === 'number') hidden.set(item.seq, typeof item.mode === 'string' ? item.mode : target.mode)
3262
3265
  }
3266
+ const hiddenVia = new Map(this.view.hiddenVia)
3267
+ for (const [via, seq] of hiddenViaOf(data.hidden)) hiddenVia.set(via, seq)
3263
3268
  this.animateOnce = true
3264
- this.publish({ pending: false, dialog: null, hidden, loaded: true, loadError: false })
3269
+ this.publish({ pending: false, dialog: null, hidden, hiddenVia, loaded: true, loadError: false })
3265
3270
  } catch {
3266
3271
  this.publish({ pending: false, failure: 'generic' })
3267
3272
  }
@@ -3419,9 +3424,27 @@ window.__ModuleLoader__.load({
3419
3424
  return seq === target.seq ? target : { ...target, seq }
3420
3425
  }
3421
3426
 
3422
- function isRowHidden(hidden, seqs) {
3427
+ // Index a deletion by the REPLACEMENT it landed, not only by the seq it
3428
+ // shadowed. A deleted message leaves a carrier standing in its row, and the
3429
+ // transcript row names the live node (the carrier), so a row checked against
3430
+ // 'hidden' alone never matches: the deletion succeeds, the model context
3431
+ // loses the message, and the row is simply never hidden - an empty bubble
3432
+ // left behind. The link is already on every hidden entry ('replacement'), so
3433
+ // it is indexed here rather than derived from 'edits': that chain
3434
+ // deliberately excludes this plugin's own carriers (they rewrite context,
3435
+ // not content) and so cannot bridge the gap.
3436
+ function hiddenViaOf(items) {
3437
+ const via = new Map()
3438
+ for (const item of Array.isArray(items) ? items : []) {
3439
+ if (item && typeof item.seq === 'number' && typeof item.replacement === 'number') via.set(item.replacement, item.seq)
3440
+ }
3441
+ return via
3442
+ }
3443
+
3444
+ function isRowHidden(hidden, hiddenVia, seqs) {
3423
3445
  for (const seq of seqs) {
3424
3446
  if (hidden.has(seq)) return true
3447
+ if (hiddenVia !== undefined && hiddenVia.has(seq)) return true
3425
3448
  }
3426
3449
  return false
3427
3450
  }
@@ -3684,13 +3707,13 @@ window.__ModuleLoader__.load({
3684
3707
  const anchor = anchorKey === undefined ? undefined : snapshot.nodes.get(anchorKey)
3685
3708
  if (anchor !== undefined) {
3686
3709
  const seqs = expandSeqs(seqsFor(anchor), view.edits)
3687
- setRowHidden(row, isRowHidden(view.hidden, seqs), animate)
3710
+ setRowHidden(row, isRowHidden(view.hidden, view.hiddenVia, seqs), animate)
3688
3711
  }
3689
3712
  continue
3690
3713
  }
3691
3714
  const seqs = expandSeqs(seqsFor(node), view.edits)
3692
3715
  for (const seq of seqs) if (typeof seq === 'number' && seq > maxSeq) maxSeq = seq
3693
- const hidden = isRowHidden(view.hidden, seqs)
3716
+ const hidden = isRowHidden(view.hidden, view.hiddenVia, seqs)
3694
3717
  setRowHidden(row, hidden, animate)
3695
3718
  const target = hidden ? null : liveTarget(targetFor(node), view.edits)
3696
3719
  const covered = target !== null || slotCoversRow(node)
@@ -4795,7 +4818,7 @@ window.__ModuleLoader__.load({
4795
4818
  const NS = 'dsh-as-aistudio'
4796
4819
 
4797
4820
  /** Keep in sync with package.json and src/components.js. */
4798
- const PLUGIN_VERSION = '0.2.4'
4821
+ const PLUGIN_VERSION = '0.2.5'
4799
4822
 
4800
4823
  /** Presence marker the live verifier reads. */
4801
4824
  const DEBUG_KEY = '__DSH_AS_AISTUDIO__'
package/src/components.js CHANGED
@@ -19,7 +19,7 @@
19
19
  export const STUDIO_ID = 'dsh-as-aistudio'
20
20
 
21
21
  /** Keep in sync with package.json and src/client.js. */
22
- export const STUDIO_VERSION = '0.2.4'
22
+ export const STUDIO_VERSION = '0.2.5'
23
23
 
24
24
  /**
25
25
  * The AI Studio action strip this studio composes, in the order the user meets
package/src/studio.js CHANGED
@@ -49,7 +49,7 @@ window.__ModuleLoader__.load({
49
49
  const NS = 'dsh-as-aistudio'
50
50
 
51
51
  /** Keep in sync with package.json and src/components.js. */
52
- const PLUGIN_VERSION = '0.2.4'
52
+ const PLUGIN_VERSION = '0.2.5'
53
53
 
54
54
  /** Presence marker the live verifier reads. */
55
55
  const DEBUG_KEY = '__DSH_AS_AISTUDIO__'
@@ -1,6 +1,6 @@
1
1
  // dsh-delete-turn browser half, VENDORED by tools/vendor.mjs - DO NOT EDIT.
2
2
  //
3
- // Source: ../dsh-delete-turn/src/client.js (v0.1.7, sha256 516518d8b6b8e5e4)
3
+ // Source: ../dsh-delete-turn/src/client.js (v0.1.8, sha256 09de24fd97ecd4ec)
4
4
  // Regenerate with `npm run vendor`; test/vendor-sync.test.js fails when this is stale.
5
5
  //
6
6
  // The component registers its factory with the host module loader; inside this
@@ -154,6 +154,8 @@ export default (require) => {
154
154
  this.animateOnce = false
155
155
  this.view = Object.freeze({
156
156
  hidden: new Map(),
157
+ // replacement seq -> the seq it was recorded as shadowing. See hiddenViaOf.
158
+ hiddenVia: new Map(),
157
159
  surface: new Set(),
158
160
  replyTurns: new Set(),
159
161
  edits: new Map(),
@@ -218,6 +220,7 @@ export default (require) => {
218
220
  for (const item of Array.isArray(data.hidden) ? data.hidden : []) {
219
221
  if (item && typeof item.seq === 'number') hidden.set(item.seq, typeof item.mode === 'string' ? item.mode : 'message')
220
222
  }
223
+ const hiddenVia = hiddenViaOf(data.hidden)
221
224
  const surface = new Set()
222
225
  for (const seq of Array.isArray(data.surface) ? data.surface : []) surface.add(seq)
223
226
  const replyTurns = new Set()
@@ -231,7 +234,7 @@ export default (require) => {
231
234
  if (typeof turn === 'number') markerTurns.add(turn)
232
235
  }
233
236
  const surfaceThrough = typeof data.lastSeq === 'number' ? data.lastSeq : -1
234
- this.publish({ hidden, surface, replyTurns, edits, markerTurns, surfaceReady: true, surfaceThrough, loaded: true, loadError: false })
237
+ this.publish({ hidden, hiddenVia, surface, replyTurns, edits, markerTurns, surfaceReady: true, surfaceThrough, loaded: true, loadError: false })
235
238
  })
236
239
  .catch(() => {
237
240
  this.publish({ loadError: true })
@@ -283,8 +286,10 @@ export default (require) => {
283
286
  for (const item of Array.isArray(data.hidden) ? data.hidden : []) {
284
287
  if (item && typeof item.seq === 'number') hidden.set(item.seq, typeof item.mode === 'string' ? item.mode : target.mode)
285
288
  }
289
+ const hiddenVia = new Map(this.view.hiddenVia)
290
+ for (const [via, seq] of hiddenViaOf(data.hidden)) hiddenVia.set(via, seq)
286
291
  this.animateOnce = true
287
- this.publish({ pending: false, dialog: null, hidden, loaded: true, loadError: false })
292
+ this.publish({ pending: false, dialog: null, hidden, hiddenVia, loaded: true, loadError: false })
288
293
  } catch {
289
294
  this.publish({ pending: false, failure: 'generic' })
290
295
  }
@@ -442,9 +447,27 @@ export default (require) => {
442
447
  return seq === target.seq ? target : { ...target, seq }
443
448
  }
444
449
 
445
- function isRowHidden(hidden, seqs) {
450
+ // Index a deletion by the REPLACEMENT it landed, not only by the seq it
451
+ // shadowed. A deleted message leaves a carrier standing in its row, and the
452
+ // transcript row names the live node (the carrier), so a row checked against
453
+ // 'hidden' alone never matches: the deletion succeeds, the model context
454
+ // loses the message, and the row is simply never hidden - an empty bubble
455
+ // left behind. The link is already on every hidden entry ('replacement'), so
456
+ // it is indexed here rather than derived from 'edits': that chain
457
+ // deliberately excludes this plugin's own carriers (they rewrite context,
458
+ // not content) and so cannot bridge the gap.
459
+ function hiddenViaOf(items) {
460
+ const via = new Map()
461
+ for (const item of Array.isArray(items) ? items : []) {
462
+ if (item && typeof item.seq === 'number' && typeof item.replacement === 'number') via.set(item.replacement, item.seq)
463
+ }
464
+ return via
465
+ }
466
+
467
+ function isRowHidden(hidden, hiddenVia, seqs) {
446
468
  for (const seq of seqs) {
447
469
  if (hidden.has(seq)) return true
470
+ if (hiddenVia !== undefined && hiddenVia.has(seq)) return true
448
471
  }
449
472
  return false
450
473
  }
@@ -707,13 +730,13 @@ export default (require) => {
707
730
  const anchor = anchorKey === undefined ? undefined : snapshot.nodes.get(anchorKey)
708
731
  if (anchor !== undefined) {
709
732
  const seqs = expandSeqs(seqsFor(anchor), view.edits)
710
- setRowHidden(row, isRowHidden(view.hidden, seqs), animate)
733
+ setRowHidden(row, isRowHidden(view.hidden, view.hiddenVia, seqs), animate)
711
734
  }
712
735
  continue
713
736
  }
714
737
  const seqs = expandSeqs(seqsFor(node), view.edits)
715
738
  for (const seq of seqs) if (typeof seq === 'number' && seq > maxSeq) maxSeq = seq
716
- const hidden = isRowHidden(view.hidden, seqs)
739
+ const hidden = isRowHidden(view.hidden, view.hiddenVia, seqs)
717
740
  setRowHidden(row, hidden, animate)
718
741
  const target = hidden ? null : liveTarget(targetFor(node), view.edits)
719
742
  const covered = target !== null || slotCoversRow(node)