dsh-conversation-aurora 0.2.0 → 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/README.md CHANGED
@@ -30,12 +30,13 @@
30
30
 
31
31
  | 快捷键 | 作用 |
32
32
  | --- | --- |
33
- | `Alt+T` | 换下一个背景(8 个循环,选择会记住) |
33
+ | `Alt+T` | 换到下一个背景,并**固定**住(8 个循环,选择会记住) |
34
+ | `Alt+R` | 回到**随机**,并立刻换一个(清掉固定值) |
34
35
  | `Alt+D` | 浅色 / 深色主题互换(走宿主 `theme.setTheme`,与「设置 → 外观」同步) |
35
36
 
36
37
  **它只在"你干活"的时候出现**:任何工具在跑、或回合进行中,背景才播放;空闲、切会话、窗口隐藏时立刻停掉 `requestAnimationFrame` 并淡出,两侧恢复普通白纸。
37
38
 
38
- **每个会话自动分配不同主题**(按 `data-conversation-session` 哈希)。按过 `Alt+T` 之后会变成全局固定主题;想恢复"每会话自动",删掉 `localStorage` 里的 `dsh.conversation-aurora.theme` 即可。
39
+ **每个回合随机一个主题**:你发一条消息、我把这一轮做完 = 一个回合;回合内背景不变(中间停顿多久都不换),下一个回合重新随机,且不会和上一个重复。`Alt+T` 固定某个主题,`Alt+R` 回到随机并换一个。固定值存在 `localStorage` 的 `dsh.conversation-aurora.theme`。
39
40
 
40
41
  ---
41
42
 
@@ -82,7 +83,7 @@
82
83
 
83
84
  ## 已知限制
84
85
 
85
- 1. **依赖宿主的 DOM 语义属性**:`[data-conversation-content]` / `[data-tool][data-state]` / `[data-chat-running]` / `[data-streaming]` / `[data-conversation-session]`。DSH 大版本升级后如果这些改名,插件会安静地什么都不做(不会报错、不会挡交互)。
86
+ 1. **依赖宿主的 DOM 语义属性**:`[data-conversation-content]` / `[data-tool][data-state]` / `[data-chat-running]` / `[data-streaming]`。DSH 大版本升级后如果这些改名,插件会安静地什么都不做(不会报错、不会挡交互)。
86
87
  2. **没有"背景层"插槽**:DSH 目前没有这个扩展点,所以背景层是挂在对话画布元素内部的(`z-index:-1`)。这是刻意偏离「只能注册进 slot」的地方,随插件停用一起消失。
87
88
  3. **切会话会静默 600ms**:避免把上一轮残留的工具行当成"正在工作",看到几秒特效。
88
89
  4. **减少动态效果**:系统开启后只画一帧静止画面,不跑 `requestAnimationFrame`。
package/client.js CHANGED
@@ -28,6 +28,10 @@ window.__ModuleLoader__.load({
28
28
  remountWindowMs: 2000,
29
29
  /** 切会话后的静默期,避免把上一轮的残留行当成"正在工作"。 */
30
30
  switchQuietMs: 600,
31
+ /** Alt+T 换主题。 */
32
+ themeKey: 't',
33
+ /** Alt+R 回到随机,并给这个回合换一个。 */
34
+ randomKey: 'r',
31
35
  };
32
36
 
33
37
  const NS = 'dsh-stage';
@@ -97,15 +101,6 @@ body[data-ds-dark-theme] .dsh-stage__veil{
97
101
  }
98
102
  }
99
103
 
100
- function hashString(text) {
101
- let h = 2166136261;
102
- for (let i = 0; i < text.length; i += 1) {
103
- h ^= text.charCodeAt(i);
104
- h = Math.imul(h, 16777619);
105
- }
106
- return h >>> 0;
107
- }
108
-
109
104
  /** 浅色用墨色 + multiply;深色用亮色 + lighter。 */
110
105
  function stagePalette() {
111
106
  const dark = document.body.hasAttribute('data-ds-dark-theme');
@@ -760,6 +755,7 @@ function themeWarp(g) {
760
755
  let sessionKey = null;
761
756
  let quietUntil = 0;
762
757
  let forcedTheme = readStore(STORAGE.theme, '');
758
+ let activeTheme = '';
763
759
  let stageRaf = 0;
764
760
  let stageTheme = null;
765
761
 
@@ -794,9 +790,28 @@ function themeWarp(g) {
794
790
  if (previous !== canvas) resetForSwitch(previous !== null);
795
791
  }
796
792
 
793
+ /**
794
+ * 回合的起点:新的 [data-streaming] / [data-chat-running] 元素出现,
795
+ * 或者从空闲重新开工。两者任一成立就算新回合,换一个主题。
796
+ */
797
+ const turnMarks = new WeakSet();
798
+
799
+ function turnIsNew(running) {
800
+ let fresh = false;
801
+ const list = document.querySelectorAll('[data-streaming], [data-chat-running]');
802
+ for (let i = 0; i < list.length; i += 1) {
803
+ const el = list[i];
804
+ if (!turnMarks.has(el)) {
805
+ turnMarks.add(el);
806
+ fresh = true;
807
+ }
808
+ }
809
+ return running && (fresh || !busy);
810
+ }
811
+
797
812
  /**
798
813
  * 切会话:上一轮的工具行可能还挂着,历史里的失败行也会被当成新事件。
799
- * 清空跟踪表、静默一小段时间,等新会话渲染稳定。
814
+ * 清空跟踪表、静默一小段时间,等新会话渲染稳定;顺便取该对话的主题。
800
815
  */
801
816
  function resetForSwitch(quiet) {
802
817
  tracked.clear();
@@ -807,6 +822,7 @@ function themeWarp(g) {
807
822
  }
808
823
  quietUntil = quiet ? Date.now() + TUNE.switchQuietMs : 0;
809
824
  sessionKey = host ? host.getAttribute('data-conversation-session') || '' : null;
825
+ activeTheme = '';
810
826
  syncStage();
811
827
  }
812
828
 
@@ -870,12 +886,31 @@ function themeWarp(g) {
870
886
 
871
887
  /* ---------- 背景播放 ---------- */
872
888
 
873
- function stageThemeId() {
874
- for (let i = 0; i < STAGE_THEMES.length; i += 1) {
875
- if (forcedTheme && STAGE_THEMES[i].id === forcedTheme) return forcedTheme;
876
- }
877
- const key = (host && host.getAttribute('data-conversation-session')) || 'default';
878
- return STAGE_THEMES[hashString(key) % STAGE_THEMES.length].id;
889
+ /**
890
+ * 主题跟着「回合」走:一个回合一个主题,回合内怎么停顿都不变,
891
+ * 下一个回合(你发新消息)重新随机。
892
+ * 按 Alt+T 固定过主题时,所有回合都用它。
893
+ */
894
+ let lastAssigned = '';
895
+
896
+ function themeIds() {
897
+ const ids = [];
898
+ for (let i = 0; i < STAGE_THEMES.length; i += 1) ids.push(STAGE_THEMES[i].id);
899
+ return ids;
900
+ }
901
+
902
+ /** 随机挑一个,尽量不要和上一个回合重复。 */
903
+ function randomThemeId() {
904
+ const ids = themeIds();
905
+ let id = ids[(Math.random() * ids.length) | 0];
906
+ if (ids.length > 1 && id === lastAssigned) id = ids[(ids.indexOf(id) + 1) % ids.length];
907
+ lastAssigned = id;
908
+ return id;
909
+ }
910
+
911
+ /** 这个回合该用哪个主题(固定值优先)。 */
912
+ function themeForTurn() {
913
+ return forcedTheme || activeTheme || (activeTheme = randomThemeId());
879
914
  }
880
915
 
881
916
  function stopStage() {
@@ -903,7 +938,9 @@ function themeWarp(g) {
903
938
  const pal = stagePalette();
904
939
  ctx.globalCompositeOperation = pal.blend;
905
940
 
906
- const id = stageThemeId();
941
+ const id = themeForTurn();
942
+ activeTheme = id;
943
+ stage.dataset.theme = id;
907
944
  let factory = STAGE_THEMES[0];
908
945
  for (let i = 0; i < STAGE_THEMES.length; i += 1) {
909
946
  if (STAGE_THEMES[i].id === id) factory = STAGE_THEMES[i];
@@ -941,13 +978,31 @@ function themeWarp(g) {
941
978
  if (!stageRaf) startStage();
942
979
  }
943
980
 
981
+ /** Alt+T:手动切到下一个,并固定住。 */
944
982
  function cycleTheme() {
945
- const ids = [];
946
- for (let i = 0; i < STAGE_THEMES.length; i += 1) ids.push(STAGE_THEMES[i].id);
983
+ const ids = themeIds();
947
984
  let at = ids.indexOf(forcedTheme);
948
- if (at < 0) at = ids.indexOf(stageThemeId());
985
+ if (at < 0) at = ids.indexOf(activeTheme);
986
+ if (at < 0) at = 0;
949
987
  forcedTheme = ids[(at + 1) % ids.length];
950
988
  writeStore(STORAGE.theme, forcedTheme);
989
+ activeTheme = forcedTheme;
990
+ restartIfPlaying();
991
+ }
992
+
993
+ /** Alt+R:回到随机,并给这个回合换一个新的。 */
994
+ function randomTheme() {
995
+ forcedTheme = '';
996
+ try {
997
+ window.localStorage.removeItem(STORAGE.theme);
998
+ } catch {
999
+ /* 隐私模式:本来就没写进去 */
1000
+ }
1001
+ activeTheme = randomThemeId();
1002
+ restartIfPlaying();
1003
+ }
1004
+
1005
+ function restartIfPlaying() {
951
1006
  if (stageRaf || busy) {
952
1007
  stopStage();
953
1008
  if (busy && !document.hidden) startStage();
@@ -975,7 +1030,15 @@ function themeWarp(g) {
975
1030
  }
976
1031
 
977
1032
  prune();
978
- setBusy(detectBusy());
1033
+ const running = detectBusy();
1034
+ if (turnIsNew(running)) {
1035
+ activeTheme = forcedTheme || randomThemeId();
1036
+ if (stageRaf) {
1037
+ stopStage();
1038
+ startStage();
1039
+ }
1040
+ }
1041
+ setBusy(running);
979
1042
  } catch {
980
1043
  /* 任何一步失败都只是不生效,不影响宿主 */
981
1044
  }
@@ -1029,8 +1092,12 @@ function themeWarp(g) {
1029
1092
 
1030
1093
  function onKeyDown(event) {
1031
1094
  if (!event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
1032
- if (String(event.key || '').toLowerCase() !== TUNE.themeKey) return;
1033
- cycleTheme();
1095
+ const key = String(event.key || '').toLowerCase();
1096
+ if (key === TUNE.themeKey) {
1097
+ cycleTheme();
1098
+ return;
1099
+ }
1100
+ if (key === TUNE.randomKey) randomTheme();
1034
1101
  }
1035
1102
 
1036
1103
  /* ---------- 启动 ---------- */
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "Conversation Backgrounds",
4
- "description": "Full-canvas animated backgrounds: starfield / code rain / circuit / cat / girl / racer / snow / bubbles. Alt+T next theme, Alt+D light/dark. Plays only while the agent is working."
4
+ "description": "Full-canvas animated backgrounds: starfield / code rain / circuit / cat / girl / racer / snow / bubbles. One random theme per turn, stable within the turn. Alt+T pins a theme, Alt+R back to random, Alt+D light/dark."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "对话区动效背景",
4
- "description": "对话区整屏动效背景:星空 / 代码雨 / 电路 / 小猫 / 小女生 / 赛车 / 飘雪 / 肥皂泡。快捷键 Alt+T 换背景、Alt+D 切明暗;只在 Agent 干活时播放。"
4
+ "description": "对话区整屏动效背景:星空 / 代码雨 / 电路 / 小猫 / 小女生 / 赛车 / 飘雪 / 肥皂泡。每个回合随机一个,回合内不变。Alt+T 固定主题、Alt+R 回到随机,Alt+D 切明暗。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-conversation-aurora",
3
- "version": "0.2.0",
3
+ "version": "0.2.5",
4
4
  "description": "Full-canvas animated backgrounds for the DSH conversation pane: starfield, code rain, circuits, cat, girl, racer, snow, bubbles.",
5
5
  "type": "module",
6
6
  "license": "MIT",