dsh-conversation-aurora 0.2.1 → 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
- **每次开始干活随机换一个主题**(8 个里随机,且不会连着重复)。按过 `Alt+T` 之后会变成全局固定主题;想恢复"每会话自动",删掉 `localStorage` 里的 `dsh.conversation-aurora.theme` 即可。
39
+ **每个回合随机一个主题**:你发一条消息、我把这一轮做完 = 一个回合;回合内背景不变(中间停顿多久都不换),下一个回合重新随机,且不会和上一个重复。`Alt+T` 固定某个主题,`Alt+R` 回到随机并换一个。固定值存在 `localStorage` 的 `dsh.conversation-aurora.theme`。
39
40
 
40
41
  ---
41
42
 
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';
@@ -786,9 +790,28 @@ function themeWarp(g) {
786
790
  if (previous !== canvas) resetForSwitch(previous !== null);
787
791
  }
788
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
+
789
812
  /**
790
813
  * 切会话:上一轮的工具行可能还挂着,历史里的失败行也会被当成新事件。
791
- * 清空跟踪表、静默一小段时间,等新会话渲染稳定。
814
+ * 清空跟踪表、静默一小段时间,等新会话渲染稳定;顺便取该对话的主题。
792
815
  */
793
816
  function resetForSwitch(quiet) {
794
817
  tracked.clear();
@@ -799,6 +822,7 @@ function themeWarp(g) {
799
822
  }
800
823
  quietUntil = quiet ? Date.now() + TUNE.switchQuietMs : 0;
801
824
  sessionKey = host ? host.getAttribute('data-conversation-session') || '' : null;
825
+ activeTheme = '';
802
826
  syncStage();
803
827
  }
804
828
 
@@ -863,18 +887,32 @@ function themeWarp(g) {
863
887
  /* ---------- 背景播放 ---------- */
864
888
 
865
889
  /**
866
- * 每次开工重新挑一个主题(随机,且不和上一次重复)。
867
- * 按 Alt+T 固定过主题时,就一直用它。
890
+ * 主题跟着「回合」走:一个回合一个主题,回合内怎么停顿都不变,
891
+ * 下一个回合(你发新消息)重新随机。
892
+ * 按 Alt+T 固定过主题时,所有回合都用它。
868
893
  */
869
- function pickTheme() {
894
+ let lastAssigned = '';
895
+
896
+ function themeIds() {
870
897
  const ids = [];
871
898
  for (let i = 0; i < STAGE_THEMES.length; i += 1) ids.push(STAGE_THEMES[i].id);
872
- if (forcedTheme && ids.indexOf(forcedTheme) >= 0) return forcedTheme;
899
+ return ids;
900
+ }
901
+
902
+ /** 随机挑一个,尽量不要和上一个回合重复。 */
903
+ function randomThemeId() {
904
+ const ids = themeIds();
873
905
  let id = ids[(Math.random() * ids.length) | 0];
874
- if (ids.length > 1 && id === activeTheme) id = ids[(ids.indexOf(id) + 1) % ids.length];
906
+ if (ids.length > 1 && id === lastAssigned) id = ids[(ids.indexOf(id) + 1) % ids.length];
907
+ lastAssigned = id;
875
908
  return id;
876
909
  }
877
910
 
911
+ /** 这个回合该用哪个主题(固定值优先)。 */
912
+ function themeForTurn() {
913
+ return forcedTheme || activeTheme || (activeTheme = randomThemeId());
914
+ }
915
+
878
916
  function stopStage() {
879
917
  if (stageRaf) window.cancelAnimationFrame(stageRaf);
880
918
  stageRaf = 0;
@@ -900,8 +938,9 @@ function themeWarp(g) {
900
938
  const pal = stagePalette();
901
939
  ctx.globalCompositeOperation = pal.blend;
902
940
 
903
- const id = activeTheme || pickTheme();
941
+ const id = themeForTurn();
904
942
  activeTheme = id;
943
+ stage.dataset.theme = id;
905
944
  let factory = STAGE_THEMES[0];
906
945
  for (let i = 0; i < STAGE_THEMES.length; i += 1) {
907
946
  if (STAGE_THEMES[i].id === id) factory = STAGE_THEMES[i];
@@ -936,21 +975,34 @@ function themeWarp(g) {
936
975
  return;
937
976
  }
938
977
  stage.dataset.on = '1';
939
- if (!stageRaf) {
940
- activeTheme = pickTheme();
941
- startStage();
942
- }
978
+ if (!stageRaf) startStage();
943
979
  }
944
980
 
981
+ /** Alt+T:手动切到下一个,并固定住。 */
945
982
  function cycleTheme() {
946
- const ids = [];
947
- for (let i = 0; i < STAGE_THEMES.length; i += 1) ids.push(STAGE_THEMES[i].id);
948
- let at = ids.indexOf(activeTheme);
949
- if (at < 0) at = ids.indexOf(forcedTheme);
983
+ const ids = themeIds();
984
+ let at = ids.indexOf(forcedTheme);
985
+ if (at < 0) at = ids.indexOf(activeTheme);
950
986
  if (at < 0) at = 0;
951
987
  forcedTheme = ids[(at + 1) % ids.length];
952
988
  writeStore(STORAGE.theme, forcedTheme);
953
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() {
954
1006
  if (stageRaf || busy) {
955
1007
  stopStage();
956
1008
  if (busy && !document.hidden) startStage();
@@ -978,7 +1030,15 @@ function themeWarp(g) {
978
1030
  }
979
1031
 
980
1032
  prune();
981
- 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);
982
1042
  } catch {
983
1043
  /* 任何一步失败都只是不生效,不影响宿主 */
984
1044
  }
@@ -1032,8 +1092,12 @@ function themeWarp(g) {
1032
1092
 
1033
1093
  function onKeyDown(event) {
1034
1094
  if (!event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
1035
- if (String(event.key || '').toLowerCase() !== TUNE.themeKey) return;
1036
- cycleTheme();
1095
+ const key = String(event.key || '').toLowerCase();
1096
+ if (key === TUNE.themeKey) {
1097
+ cycleTheme();
1098
+ return;
1099
+ }
1100
+ if (key === TUNE.randomKey) randomTheme();
1037
1101
  }
1038
1102
 
1039
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, a new one each time the agent starts working. Alt+T next theme, Alt+D light/dark."
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.1",
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",