@timurproko/a1 0.2.7-dev.1252 → 0.2.7-dev.1255

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.
Files changed (253) hide show
  1. package/bin/ui.js +3 -3
  2. package/dist/app/session-shell/clipboard-diagnostics.d.ts +2 -2
  3. package/dist/app/session-shell/clipboard-image.d.ts +0 -1
  4. package/dist/app/session-shell/clipboard-image.js +1 -1
  5. package/dist/app/session-shell/dialog-coordinator.d.ts +6 -5
  6. package/dist/app/session-shell/dialog-coordinator.js +3 -4
  7. package/dist/app/session-shell/dock-chrome.d.ts +11 -6
  8. package/dist/app/session-shell/dock-chrome.js +5 -8
  9. package/dist/app/session-shell/editor-hyperlink-budget.d.ts +0 -2
  10. package/dist/app/session-shell/editor-hyperlink-budget.js +1 -1
  11. package/dist/app/session-shell/exit-notice.d.ts +2 -2
  12. package/dist/app/session-shell/exit-notice.js +1 -1
  13. package/dist/app/session-shell/extension-surface.d.ts +8 -7
  14. package/dist/app/session-shell/helper-pool.d.ts +3 -4
  15. package/dist/app/session-shell/helper-pool.js +1 -1
  16. package/dist/app/session-shell/image-preparation-client.d.ts +4 -3
  17. package/dist/app/session-shell/image-preparation.d.ts +0 -2
  18. package/dist/app/session-shell/image-preparation.js +2 -2
  19. package/dist/app/session-shell/image-source.d.ts +2 -3
  20. package/dist/app/session-shell/image-source.js +2 -2
  21. package/dist/app/session-shell/input-surface-switch.d.ts +9 -7
  22. package/dist/app/session-shell/input-surface-switch.js +1 -2
  23. package/dist/app/session-shell/login-interaction.d.ts +5 -4
  24. package/dist/app/session-shell/login-interaction.js +2 -3
  25. package/dist/app/session-shell/paste-executor.d.ts +4 -8
  26. package/dist/app/session-shell/paste-executor.js +2 -2
  27. package/dist/app/session-shell/paste-protocol.d.ts +2 -2
  28. package/dist/app/session-shell/path-chip-presentation.d.ts +0 -5
  29. package/dist/app/session-shell/path-chip-presentation.js +3 -3
  30. package/dist/app/session-shell/prompt-chips.d.ts +8 -7
  31. package/dist/app/session-shell/prompt-history-controller.d.ts +3 -3
  32. package/dist/app/session-shell/prompt-submission.d.ts +5 -2
  33. package/dist/app/session-shell/prompt-submission.js +4 -5
  34. package/dist/app/session-shell/quit-outro-effects.d.ts +3 -3
  35. package/dist/app/session-shell/quit-outro-effects.js +1 -4
  36. package/dist/app/session-shell/quit-outro.d.ts +9 -12
  37. package/dist/app/session-shell/quit-outro.js +11 -11
  38. package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
  39. package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
  40. package/dist/app/session-shell/response-copy-transport.js +1 -1
  41. package/dist/app/session-shell/session-lifecycle.d.ts +2 -1
  42. package/dist/app/session-shell/session-shell-root.d.ts +74 -89
  43. package/dist/app/session-shell/session-shell-root.js +81 -113
  44. package/dist/app/session-shell/session-shell.d.ts +13 -16
  45. package/dist/app/session-shell/session-shell.js +144 -268
  46. package/dist/app/session-shell/session-viewport-controller.d.ts +7 -6
  47. package/dist/app/session-shell/session-viewport-controller.js +2 -2
  48. package/dist/app/session-shell/settings-bindings.d.ts +24 -2
  49. package/dist/app/session-shell/settings-bindings.js +24 -0
  50. package/dist/app/session-shell/share-operation.d.ts +3 -3
  51. package/dist/app/session-shell/share-operation.js +4 -6
  52. package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
  53. package/dist/app/session-shell/shell-clipboard.js +119 -0
  54. package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
  55. package/dist/app/session-shell/shell-editor-factory.js +13 -15
  56. package/dist/app/session-shell/shell-host.d.ts +16 -17
  57. package/dist/app/session-shell/shell-host.js +18 -19
  58. package/dist/app/session-shell/shell-layout.d.ts +2 -2
  59. package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
  60. package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
  61. package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
  62. package/dist/app/session-shell/suggestion-surface.js +1 -1
  63. package/dist/app/session-shell/system-clipboard.d.ts +2 -14
  64. package/dist/app/session-shell/system-clipboard.js +2 -7
  65. package/dist/app/session-shell/transcript-document.d.ts +11 -8
  66. package/dist/app/session-shell/transcript-document.js +13 -14
  67. package/dist/app/session-shell/viewport-layout.d.ts +2 -2
  68. package/dist/app/session-shell/workflow-output.d.ts +5 -3
  69. package/dist/app/session-shell/workflow-output.js +13 -15
  70. package/dist/cli/package-messages.d.ts +0 -1
  71. package/dist/cli/package-messages.js +1 -1
  72. package/dist/cli/session-context.d.ts +1 -1
  73. package/dist/cli/uninstall.d.ts +3 -2
  74. package/dist/composition/owned-settings.d.ts +2 -1
  75. package/dist/composition/owned-settings.js +0 -3
  76. package/dist/composition/owned-ui.d.ts +1 -1
  77. package/dist/composition/owned-ui.js +27 -27
  78. package/dist/composition/reference-selection-options.d.ts +1 -1
  79. package/dist/composition/reference-selection-options.js +1 -1
  80. package/dist/composition/session-runtime-registry.d.ts +2 -1
  81. package/dist/composition/settings-route-host.js +3 -10
  82. package/dist/composition/settings-search-input.d.ts +2 -1
  83. package/dist/composition/startup-route.d.ts +2 -2
  84. package/dist/composition/startup-route.js +2 -2
  85. package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
  86. package/dist/contracts/owned-ui/index.d.ts +1 -0
  87. package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
  88. package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
  89. package/dist/contracts/owned-ui/shell-components.js +1 -0
  90. package/dist/contracts/presentation/component.d.ts +17 -0
  91. package/dist/contracts/presentation/component.js +8 -0
  92. package/dist/contracts/presentation/index.d.ts +6 -15
  93. package/dist/contracts/presentation/index.js +2 -0
  94. package/dist/contracts/presentation/terminal.d.ts +290 -0
  95. package/dist/contracts/presentation/terminal.js +1 -0
  96. package/dist/contracts/presentation/theme.d.ts +46 -0
  97. package/dist/contracts/presentation/theme.js +1 -0
  98. package/dist/features/launch/development-launch.js +1 -1
  99. package/dist/features/launch/runtime-selection.d.ts +2 -2
  100. package/dist/features/launch/runtime-selection.js +2 -2
  101. package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
  102. package/dist/features/owned-ui/project-trust-prompt.js +1 -1
  103. package/dist/features/owned-ui/release-note-state.d.ts +2 -1
  104. package/dist/features/owned-ui/settings-app.js +38 -219
  105. package/dist/features/owned-ui/settings-edits.d.ts +31 -0
  106. package/dist/features/owned-ui/settings-edits.js +139 -0
  107. package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
  108. package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
  109. package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
  110. package/dist/features/prompt-history/service.d.ts +3 -2
  111. package/dist/foundation/lifecycle/claim-gate.d.ts +2 -1
  112. package/dist/foundation/release/automatic-update.d.ts +5 -14
  113. package/dist/foundation/release/automatic-update.js +14 -14
  114. package/dist/foundation/release/bootstrap.js +4 -4
  115. package/dist/foundation/release/dependency-certification.d.ts +0 -1
  116. package/dist/foundation/release/dependency-certification.js +1 -1
  117. package/dist/foundation/release/early-accent.d.ts +2 -2
  118. package/dist/foundation/release/early-accent.js +1 -1
  119. package/dist/foundation/release/index.d.ts +2 -2
  120. package/dist/foundation/release/index.js +1 -1
  121. package/dist/foundation/release/latest-release.d.ts +3 -18
  122. package/dist/foundation/release/latest-release.js +10 -10
  123. package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
  124. package/dist/foundation/release/ordinary-launch-selection.js +1 -1
  125. package/dist/foundation/release/release-gc.d.ts +3 -3
  126. package/dist/foundation/release/release-gc.js +4 -4
  127. package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +23 -23
  128. package/dist/foundation/release/{cohort-state.js → release-state.js} +8 -8
  129. package/dist/foundation/release/update-activation.js +2 -2
  130. package/dist/foundation/release/update-execution-lease.d.ts +2 -3
  131. package/dist/foundation/release/update-execution-lease.js +1 -1
  132. package/dist/foundation/release/update-launch.d.ts +2 -2
  133. package/dist/foundation/release/update.d.ts +0 -2
  134. package/dist/foundation/release/update.js +14 -14
  135. package/dist/foundation/startup/startup-budget.d.ts +5 -5
  136. package/dist/foundation/startup/startup-budget.js +2 -2
  137. package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
  138. package/dist/integrations/pi/components/index.d.ts +15 -9
  139. package/dist/integrations/pi/components/index.js +9 -5
  140. package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
  141. package/dist/integrations/pi/components/lazy-selectors.js +1 -1
  142. package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
  143. package/dist/integrations/pi/components/modal-frame.js +3 -23
  144. package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
  145. package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
  146. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
  147. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
  148. package/dist/integrations/pi/components/shell-footer-status.d.ts +2 -2
  149. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  150. package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -5
  151. package/dist/integrations/pi/components/shell-presenters-info.js +2 -1
  152. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
  153. package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -227
  154. package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
  155. package/dist/integrations/pi/components/skills-command.d.ts +1 -9
  156. package/dist/integrations/pi/components/skills-command.js +5 -5
  157. package/dist/integrations/pi/components/theme.d.ts +9 -2
  158. package/dist/integrations/pi/components/theme.js +17 -2
  159. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
  160. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  161. package/dist/integrations/pi/engine/adapter.d.ts +3 -5
  162. package/dist/integrations/pi/engine/adapter.js +3 -12
  163. package/dist/integrations/pi/engine/changelog.d.ts +2 -2
  164. package/dist/integrations/pi/engine/changelog.js +3 -3
  165. package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
  166. package/dist/integrations/pi/engine/compaction-progress.js +1 -1
  167. package/dist/integrations/pi/engine/conformance.d.ts +2 -1
  168. package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
  169. package/dist/integrations/pi/engine/default-model-setting.js +1 -2
  170. package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
  171. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
  172. package/dist/integrations/pi/engine/host.js +2 -2
  173. package/dist/integrations/pi/engine/message-values.d.ts +0 -1
  174. package/dist/integrations/pi/engine/message-values.js +1 -1
  175. package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
  176. package/dist/integrations/pi/engine/pending-delivery.js +3 -3
  177. package/dist/integrations/pi/engine/ports.d.ts +7 -1
  178. package/dist/integrations/pi/engine/ports.js +4 -0
  179. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
  180. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -2
  181. package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
  182. package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
  183. package/dist/integrations/pi/engine/public-main-entry.js +1 -1
  184. package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
  185. package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
  186. package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
  187. package/dist/integrations/pi/engine/repository-pr.js +1 -1
  188. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  189. package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
  190. package/dist/integrations/pi/engine/session-runtime.d.ts +4 -3
  191. package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
  192. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  193. package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
  194. package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
  195. package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
  196. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -1
  197. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -6
  198. package/dist/integrations/pi/engine/workflow-support.js +4 -18
  199. package/dist/integrations/pi/engine/workflows.d.ts +5 -5
  200. package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
  201. package/dist/integrations/pi/session-presenters/components.js +75 -0
  202. package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
  203. package/dist/integrations/pi/session-presenters/index.js +1 -0
  204. package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
  205. package/dist/integrations/pi/session-presenters/presentation.js +10 -0
  206. package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
  207. package/dist/integrations/pi/session-presenters/presenters.js +3 -1
  208. package/dist/integrations/pi/tui-runtime/adapter.js +21 -30
  209. package/dist/integrations/pi/tui-runtime/contracts.d.ts +22 -166
  210. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +5 -40
  211. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -0
  212. package/dist/integrations/pi/tui-runtime/index.js +1 -0
  213. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
  214. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -8
  215. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
  216. package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
  217. package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
  218. package/dist/ui/components/index.d.ts +2 -2
  219. package/dist/ui/components/index.js +1 -1
  220. package/dist/ui/components/selection-copy.d.ts +0 -1
  221. package/dist/ui/components/selection-copy.js +0 -12
  222. package/dist/ui/components/theme.d.ts +4 -19
  223. package/dist/ui/components/theme.js +6 -0
  224. package/dist/ui/settings/declarations.d.ts +2 -3
  225. package/dist/ui/settings/declarations.js +2 -3
  226. package/dist/ui/settings/early-accent.js +1 -1
  227. package/dist/ui/settings/index.d.ts +3 -5
  228. package/dist/ui/settings/index.js +2 -3
  229. package/dist/ui/settings/manager.d.ts +0 -4
  230. package/dist/ui/settings/manager.js +6 -62
  231. package/dist/ui/settings/resolution.d.ts +6 -18
  232. package/dist/ui/settings/resolution.js +4 -103
  233. package/dist/ui/settings/sections.d.ts +0 -4
  234. package/dist/ui/settings/sections.js +1 -1
  235. package/docs/architecture/boundaries.md +2 -2
  236. package/docs/architecture/internal-naming.md +2 -0
  237. package/docs/architecture/project-structure.md +4 -2
  238. package/docs/validation.md +1 -1
  239. package/package.json +3 -4
  240. package/dist/integrations/pi/components/components.d.ts +0 -4
  241. package/dist/integrations/pi/components/components.js +0 -48
  242. package/dist/integrations/pi/components/conformance.d.ts +0 -18
  243. package/dist/integrations/pi/components/conformance.js +0 -70
  244. package/dist/integrations/pi/components/shell-components.d.ts +0 -7
  245. package/dist/integrations/pi/components/shell-components.js +0 -7
  246. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
  247. package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
  248. package/dist/ui/settings/migrations.d.ts +0 -19
  249. package/dist/ui/settings/migrations.js +0 -225
  250. package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
  251. package/dist/ui/settings/progress-palette.generated.js +0 -139
  252. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
  253. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
@@ -1,5 +1,7 @@
1
1
  import { DIALOG_BACK_SHORTCUT_HINT, DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
- import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
2
+ import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollbarGeometry, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
3
+ import { SettingsScrollRail } from "./settings-scroll-rail.js";
4
+ import { SettingsEdits, changeFailure, replaceRecord, sameRecord, structuredValue } from "./settings-edits.js";
3
5
  import { SETTINGS_APP_ID } from "./settings-route.js";
4
6
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
5
7
  const SCOPE = SETTINGS_APP_ID;
@@ -14,11 +16,6 @@ const DIALOG_SCOPE = `${SETTINGS_APP_ID}-parts`;
14
16
  const MODEL_SCOPE = `${SETTINGS_APP_ID}-models`;
15
17
  const MODEL_LEVEL_SCOPE = `${SETTINGS_APP_ID}-model-levels`;
16
18
  const SCROLLBAR_TOP_INSET = 0;
17
- /** Identity of the settings list rail in the shared rail state. */
18
- const RAIL_KEY = "settings";
19
- // Compatibility: the transcript rail stays lit this long after a scroll, and repaints just after.
20
- const SCROLL_LINGER_MS = 900;
21
- const SCROLL_LINGER_REPAINT_MS = 925;
22
19
  /** What a structured value offers instead of printing itself. */
23
20
  const CONFIGURE = "configure";
24
21
  export const SETTINGS_SHORTCUTS = new ShortcutRegistry();
@@ -96,12 +93,7 @@ export class SettingsApp {
96
93
  #menu = null;
97
94
  #structured = null;
98
95
  // Invariant: pending values remain visible until the source reflects them.
99
- #pending = new Map();
100
- #undoHistory = [];
101
- #editSequence = 0;
102
- #undoRequests = 0;
103
- #undoActive = false;
104
- #closed = false;
96
+ #edits;
105
97
  #bodyTopForFrame = SETTINGS_TOP_RULE_ROWS + SETTINGS_TITLE_ROWS;
106
98
  #bodyHeightForFrame = 0;
107
99
  #panelTopForFrame = 0;
@@ -110,16 +102,12 @@ export class SettingsApp {
110
102
  #frameRows = [];
111
103
  #menuFrame = null;
112
104
  // Invariant: rail hover and drag live in the shared keyed state, as the transcript's do.
113
- #rails = new ScrollbarRails();
105
+ #rail = new SettingsScrollRail();
114
106
  // Rationale: the rail as drawn in the last frame, or null when nothing can be pointed at.
115
- #railFrame = null;
116
- // Invariant: a scroll lights the rail until this time; the timer repaints once it has passed.
117
- #activeUntil = 0;
118
- #activityTimer;
119
- #renderedScroll;
120
107
  #options;
121
108
  constructor(session, options) {
122
109
  this.#session = session;
110
+ this.#edits = new SettingsEdits(session, () => this.#structured);
123
111
  this.#options = {
124
112
  dialogPanel: options.dialogPanel ?? null,
125
113
  };
@@ -131,17 +119,14 @@ export class SettingsApp {
131
119
  };
132
120
  }
133
121
  onActivate(host) {
134
- this.#closed = false;
122
+ this.#edits.open();
135
123
  void this.#session.load().then(() => host.requestRender());
136
124
  }
137
125
  onClose(_host) {
138
- this.#closed = true;
139
- this.#undoHistory.length = 0;
140
- this.#undoRequests = 0;
126
+ this.#edits.close();
141
127
  this.#search.input?.setFocused(false);
142
128
  this.#search.input = null;
143
- this.#clearActivityTimer();
144
- this.#rails.clear();
129
+ this.#rail.dispose();
145
130
  }
146
131
  render(rect, host) {
147
132
  const theme = host.theme ?? PLAIN_THEME;
@@ -174,11 +159,7 @@ export class SettingsApp {
174
159
  this.#panelTopForFrame = topRows + contentHeight + dividerRows;
175
160
  this.#scroll = layout.scroll;
176
161
  const now = Date.now();
177
- // Rationale: every way of scrolling ends in this frame, so a moved list is noticed here
178
- // once rather than at each wheel, drag, key, and search branch.
179
- if (this.#renderedScroll !== undefined && this.#renderedScroll !== layout.scroll)
180
- this.#noteScrollActivity(host, now);
181
- this.#renderedScroll = layout.scroll;
162
+ this.#rail.noteRenderedScroll(layout.scroll, now, () => host.requestRender());
182
163
  // Invariant: auto and always keep the rail columns while the list fits, so a revealed
183
164
  // rail never reflows the rows; hidden gives the columns back to the rows.
184
165
  const appearance = this.#scrollbarAppearance();
@@ -191,22 +172,10 @@ export class SettingsApp {
191
172
  scroll: layout.scroll,
192
173
  trackHeight: Math.max(0, titleRows + bodyHeight - SCROLLBAR_TOP_INSET),
193
174
  });
194
- const presentation = scrollbarPresentation({
195
- geometry,
196
- appearance,
197
- style: this.#scrollbarStyle(),
198
- hovered: this.#rails.isHovered(RAIL_KEY),
199
- dragging: this.#rails.isDragging(RAIL_KEY),
200
- activeUntil: this.#activeUntil,
201
- now,
202
- });
203
- this.#railFrame = reservesRail && geometry !== null
204
- ? {
205
- rail: { key: RAIL_KEY, column: rect.width, rowStart: topRows + SCROLLBAR_TOP_INSET, trackHeight: geometry.trackHeight },
206
- geometry,
207
- page: layout.visible,
208
- }
209
- : null;
175
+ const presentation = this.#rail.presentation({ geometry, appearance, style: this.#scrollbarStyle(), now });
176
+ this.#rail.setFrame(reservesRail && geometry !== null
177
+ ? { rail: { column: rect.width, rowStart: topRows + SCROLLBAR_TOP_INSET, trackHeight: geometry.trackHeight }, geometry, page: layout.visible }
178
+ : null);
210
179
  const body = [];
211
180
  this.#frameRows = [];
212
181
  if (rows.length === 0) {
@@ -275,7 +244,7 @@ export class SettingsApp {
275
244
  const row = rows[selected];
276
245
  if (row?.kind === "element" && (row.value.structured || isDefaultModelEntry(row.value)))
277
246
  this.#openStructured(row.value);
278
- else if (row?.kind === "element" && typeof this.#shownValue(row.value) === "number")
247
+ else if (row?.kind === "element" && typeof this.#edits.shown(row.value) === "number")
279
248
  this.#cycle(rows, selected, 1);
280
249
  else
281
250
  this.#openMenu(rows, selected, true);
@@ -313,7 +282,7 @@ export class SettingsApp {
313
282
  this.#cycle(rows, selected, action === "next-value" ? 1 : -1);
314
283
  return { consumed: true };
315
284
  case "undo":
316
- this.#undo();
285
+ this.#edits.undo();
317
286
  return { consumed: true };
318
287
  default:
319
288
  return { consumed: false };
@@ -355,7 +324,7 @@ export class SettingsApp {
355
324
  const value = menu.choices[overRow];
356
325
  this.#closeMenu(menu);
357
326
  if (value !== undefined)
358
- this.#apply(menu.entry, value);
327
+ this.#edits.apply(menu.entry, value);
359
328
  return { consumed: true };
360
329
  }
361
330
  if (event.kind === "wheel-up" || event.kind === "wheel-down") {
@@ -380,7 +349,14 @@ export class SettingsApp {
380
349
  // cannot change its selection or act on the Settings list behind it.
381
350
  return { consumed: true, render: false };
382
351
  }
383
- const rail = this.#railPointer(event);
352
+ const rail = this.#rail.pointer(event, this.#scroll);
353
+ if (rail.scroll !== undefined)
354
+ this.#scroll = rail.scroll;
355
+ // Invariant: the rail is not a row: pointing at it lights nothing in the list.
356
+ if (rail.overRail) {
357
+ this.#hoverKey = null;
358
+ this.#hoverRegion = "label";
359
+ }
384
360
  if (rail.owned)
385
361
  return { consumed: true };
386
362
  const row = this.#frameRows.find(candidate => candidate.screenRow === event.row - 1);
@@ -410,52 +386,6 @@ export class SettingsApp {
410
386
  const changed = previousKey !== this.#hoverKey || previousRegion !== this.#hoverRegion || rail.changed;
411
387
  return { consumed: event.kind !== "motion", render: changed };
412
388
  }
413
- // Rationale: the rail takes its share of a pointer report first: hover, a thumb drag, or a
414
- // track page. Owned means the list must not see the report; changed means the rail looks different.
415
- #railPointer(event) {
416
- const frame = this.#railFrame;
417
- const wasHovered = this.#rails.isHovered(RAIL_KEY);
418
- if (frame === null) {
419
- this.#rails.clear();
420
- return { owned: false, changed: wasHovered };
421
- }
422
- const pointer = { column: event.column, row: event.row - 1 };
423
- if (this.#rails.isDragging(RAIL_KEY)) {
424
- // Invariant: a drag keeps the pointer wherever it goes until the button comes up.
425
- if (event.kind === "release")
426
- this.#rails.endDrag();
427
- else {
428
- const target = this.#rails.dragTo(frame.rail, frame.geometry, pointer);
429
- if (target !== null)
430
- this.#scroll = target;
431
- }
432
- return { owned: true, changed: true };
433
- }
434
- const over = this.#rails.notePointer([frame.rail], pointer) !== null;
435
- if (!over)
436
- return { owned: false, changed: wasHovered };
437
- // Invariant: the rail is not a row: pointing at it lights nothing in the list.
438
- this.#hoverKey = null;
439
- this.#hoverRegion = "label";
440
- if (event.kind === "press" && !this.#rails.beginDrag(frame.rail, frame.geometry, pointer)) {
441
- this.#scroll = scrollForTrackPage(frame.geometry, pointer.row - frame.rail.rowStart, this.#scroll, frame.page);
442
- }
443
- return { owned: true, changed: true };
444
- }
445
- #noteScrollActivity(host, now) {
446
- this.#activeUntil = Math.max(this.#activeUntil, now + SCROLL_LINGER_MS);
447
- this.#clearActivityTimer();
448
- this.#activityTimer = setTimeout(() => {
449
- this.#activityTimer = undefined;
450
- host.requestRender();
451
- }, SCROLL_LINGER_REPAINT_MS);
452
- this.#activityTimer.unref?.();
453
- }
454
- #clearActivityTimer() {
455
- if (this.#activityTimer !== undefined)
456
- clearTimeout(this.#activityTimer);
457
- this.#activityTimer = undefined;
458
- }
459
389
  #openMenu(rows, selected, selectCurrent = false) {
460
390
  const row = rows[selected];
461
391
  if (row === undefined || row.kind !== "element")
@@ -465,7 +395,7 @@ export class SettingsApp {
465
395
  this.#openStructured(entry);
466
396
  return;
467
397
  }
468
- const shown = this.#shownValue(entry);
398
+ const shown = this.#edits.shown(entry);
469
399
  // Rationale: a number is stepped, not picked from a list: it has its own two controls,
470
400
  // and pointing at it is not a request for anything else.
471
401
  if (typeof shown === "number")
@@ -522,7 +452,7 @@ export class SettingsApp {
522
452
  // thing under the pointer, so it stops looking like it.
523
453
  this.#hoverKey = null;
524
454
  this.#hoverRegion = "label";
525
- this.#rails.clear();
455
+ this.#rail.clear();
526
456
  const currentDefault = typeof entry.rawValue === "string" ? entry.rawValue : null;
527
457
  this.#structured = {
528
458
  entry,
@@ -557,7 +487,7 @@ export class SettingsApp {
557
487
  this.#toggleFlag(open.index);
558
488
  return { consumed: true };
559
489
  case "undo":
560
- this.#undo();
490
+ this.#edits.undo();
561
491
  return { consumed: true };
562
492
  default:
563
493
  return { consumed: true, render: false };
@@ -689,14 +619,8 @@ export class SettingsApp {
689
619
  this.#saveStructured(open, previous, forward);
690
620
  }
691
621
  #saveStructured(open, previous, forward) {
692
- const sequence = ++this.#editSequence;
693
- void this.#session.changeStructured(open.entry.backend, open.entry.id, structuredValue(open.entry, forward)).then(outcome => {
694
- const failure = changeFailure(outcome);
695
- if (failure === null) {
696
- this.#recordUndo({ kind: "structured", sequence, entry: open.entry, previous, forward });
697
- return;
698
- }
699
- // Concurrency: only roll back the dialog if no later press has moved the record on.
622
+ // Concurrency: only roll back the dialog if no later press has moved the record on.
623
+ this.#edits.saveStructured(open.entry, previous, forward, () => {
700
624
  if (this.#structured === open && sameRecord(open.record, forward))
701
625
  replaceRecord(open.record, previous);
702
626
  });
@@ -708,7 +632,7 @@ export class SettingsApp {
708
632
  const key = KEYS[data] ?? data;
709
633
  if (SETTINGS_SHORTCUTS.resolve(key, SCOPE) === "undo") {
710
634
  this.#closeMenu(menu);
711
- this.#undo();
635
+ this.#edits.undo();
712
636
  return { consumed: true };
713
637
  }
714
638
  if (key === "escape") {
@@ -725,7 +649,7 @@ export class SettingsApp {
725
649
  const value = menu.choices[menu.index];
726
650
  this.#closeMenu(menu);
727
651
  if (value !== undefined)
728
- this.#apply(menu.entry, value);
652
+ this.#edits.apply(menu.entry, value);
729
653
  }
730
654
  return { consumed: true };
731
655
  }
@@ -785,13 +709,13 @@ export class SettingsApp {
785
709
  const entry = row.value;
786
710
  if (!entry.editable)
787
711
  return;
788
- const shown = this.#shownValue(entry);
712
+ const shown = this.#edits.shown(entry);
789
713
  if (typeof shown === "number") {
790
714
  // Rationale: at the end of the range there is nothing to say: the arrow already reads
791
715
  // as unavailable, so a message would only repeat it.
792
716
  const next = steppedValue(rangeOf(entry), shown, delta);
793
717
  if (next !== null)
794
- this.#apply(entry, next);
718
+ this.#edits.apply(entry, next);
795
719
  return;
796
720
  }
797
721
  const choices = entry.choices;
@@ -802,82 +726,7 @@ export class SettingsApp {
802
726
  ? (delta > 0 ? 0 : choices.length - 1)
803
727
  : (current + delta + choices.length) % choices.length];
804
728
  if (next !== undefined)
805
- this.#apply(entry, next);
806
- }
807
- #apply(entry, value) {
808
- const key = `${entry.backend}:${entry.id}`;
809
- const previous = this.#shownValue(entry);
810
- const sequence = ++this.#editSequence;
811
- // Invariant: shown immediately so the row never lags a keypress, and so the next press
812
- // steps from here rather than from a value the source has not caught up to.
813
- this.#pending.set(key, value);
814
- void this.#session.change(entry.backend, entry.id, value).then(outcome => {
815
- const failure = changeFailure(outcome);
816
- if (failure !== null) {
817
- if (this.#pending.get(key) === value)
818
- this.#pending.delete(key);
819
- return;
820
- }
821
- if (previous !== null && previous !== value)
822
- this.#recordUndo({ kind: "scalar", sequence, entry, previous });
823
- // Concurrency: a later press may have moved on; only the last request clears itself.
824
- if (this.#pending.get(key) === value)
825
- this.#pending.delete(key);
826
- });
827
- }
828
- #shownValue(entry) {
829
- return this.#pending.get(`${entry.backend}:${entry.id}`) ?? entry.value;
830
- }
831
- #recordUndo(record) {
832
- if (this.#closed)
833
- return;
834
- const after = this.#undoHistory.findIndex(candidate => candidate.sequence > record.sequence);
835
- if (after < 0)
836
- this.#undoHistory.push(record);
837
- else
838
- this.#undoHistory.splice(after, 0, record);
839
- }
840
- #undo() {
841
- this.#undoRequests++;
842
- this.#drainUndo();
843
- }
844
- #drainUndo() {
845
- if (this.#undoActive || this.#undoRequests === 0)
846
- return;
847
- const record = this.#undoHistory.pop();
848
- if (record === undefined) {
849
- this.#undoRequests = 0;
850
- return;
851
- }
852
- this.#undoRequests--;
853
- this.#undoActive = true;
854
- if (record.kind === "scalar") {
855
- const key = `${record.entry.backend}:${record.entry.id}`;
856
- this.#pending.set(key, record.previous);
857
- void this.#session.change(record.entry.backend, record.entry.id, record.previous).then(outcome => {
858
- if (this.#pending.get(key) === record.previous)
859
- this.#pending.delete(key);
860
- this.#finishUndo(record, outcome);
861
- });
862
- return;
863
- }
864
- const open = this.#structured;
865
- if (open !== null && sameEntry(open.entry, record.entry))
866
- replaceRecord(open.record, record.previous);
867
- void this.#session.changeStructured(record.entry.backend, record.entry.id, structuredValue(record.entry, record.previous)).then(outcome => {
868
- const failure = changeFailure(outcome);
869
- if (failure !== null && this.#structured === open && open !== null && sameRecord(open.record, record.previous)) {
870
- replaceRecord(open.record, record.forward);
871
- }
872
- this.#finishUndo(record, outcome);
873
- });
874
- }
875
- #finishUndo(record, outcome) {
876
- const failure = changeFailure(outcome);
877
- if (failure !== null)
878
- this.#recordUndo(record);
879
- this.#undoActive = false;
880
- this.#drainUndo();
729
+ this.#edits.apply(entry, next);
881
730
  }
882
731
  #scrollbarSpeed() {
883
732
  const value = this.#scrollSetting("scrollbarSpeed");
@@ -895,7 +744,7 @@ export class SettingsApp {
895
744
  const entry = this.#session.sections()
896
745
  .flatMap(section => section.entries)
897
746
  .find(candidate => candidate.backend === "a1" && candidate.id === id);
898
- return entry === undefined ? this.#session.value(id) : this.#shownValue(entry);
747
+ return entry === undefined ? this.#session.value(id) : this.#edits.shown(entry);
899
748
  }
900
749
  #jump(rows, target) {
901
750
  this.#select(rows, target);
@@ -967,7 +816,7 @@ export class SettingsApp {
967
816
  }
968
817
  #viewRow(entry) {
969
818
  const key = `${entry.backend}:${entry.id}`;
970
- const shown = this.#shownValue(entry);
819
+ const shown = this.#edits.shown(entry);
971
820
  const value = entry.structured || isDefaultModelEntry(entry)
972
821
  ? CONFIGURE
973
822
  : shown === null
@@ -993,7 +842,7 @@ export class SettingsApp {
993
842
  ...(menu.entry.id === "accentColor" ? {
994
843
  previews: menu.choices.map(choice => theme.accentPreview?.(String(choice), "■") ?? ""),
995
844
  } : {}),
996
- current: this.#shownValue(menu.entry) === null ? null : displaySettingValue(menu.entry, this.#shownValue(menu.entry)),
845
+ current: this.#edits.shown(menu.entry) === null ? null : displaySettingValue(menu.entry, this.#edits.shown(menu.entry)),
997
846
  index: menu.index,
998
847
  };
999
848
  const frame = valueMenuFrame(state, {
@@ -1155,36 +1004,6 @@ function thinkingLevelDescription(level) {
1155
1004
  default: return "";
1156
1005
  }
1157
1006
  }
1158
- function structuredValue(entry, record) {
1159
- // Invariant: a choice part at its fallback is unset, so the engine's default applies.
1160
- return Object.fromEntries(Object.entries(record).filter(([key, value]) => {
1161
- const part = entry.flags.find(candidate => candidate.key === key);
1162
- return part?.choices === undefined || value !== part.fallback;
1163
- }));
1164
- }
1165
- function sameEntry(left, right) {
1166
- return left.backend === right.backend && left.id === right.id;
1167
- }
1168
- function sameRecord(left, right) {
1169
- const leftEntries = Object.entries(left);
1170
- const rightEntries = Object.entries(right);
1171
- return leftEntries.length === rightEntries.length
1172
- && leftEntries.every(([key, value]) => right[key] === value);
1173
- }
1174
- function replaceRecord(target, source) {
1175
- for (const key of Object.keys(target))
1176
- delete target[key];
1177
- Object.assign(target, source);
1178
- }
1179
- function changeFailure(outcome) {
1180
- if (outcome.failure !== null)
1181
- return outcome.failure;
1182
- if (outcome.status === "failed")
1183
- return "the effect failed";
1184
- if (outcome.status === "unavailable")
1185
- return outcome.limitationReason ?? "the setting is unavailable";
1186
- return outcome.limitationReason;
1187
- }
1188
1007
  function isDefaultModelEntry(entry) {
1189
1008
  return entry.backend === "agent" && entry.id === "defaultModel";
1190
1009
  }
@@ -0,0 +1,31 @@
1
+ import type { OwnedUiSettingValue, OwnedUiSettingsChangeOutcome, OwnedUiSettingsEntry, OwnedSettingsManager } from "../../ui/settings/index.js";
2
+ type SettingsRecord = Record<string, boolean | string>;
3
+ /** The structured dialog an undo may need to roll back in place; null when none is open. */
4
+ export interface OpenStructuredRecord {
5
+ readonly entry: OwnedUiSettingsEntry;
6
+ readonly record: SettingsRecord;
7
+ }
8
+ /**
9
+ * The settings screen's edits: values shown ahead of the source, and an undo history ordered by edit
10
+ * sequence. Undo requests drain one at a time so each restores against the result of the one before.
11
+ */
12
+ export declare class SettingsEdits {
13
+ #private;
14
+ constructor(session: Pick<OwnedSettingsManager, "change" | "changeStructured">, openStructured: () => OpenStructuredRecord | null);
15
+ /** The value a row shows: a pending edit, or the source's value. */
16
+ shown(entry: OwnedUiSettingsEntry): OwnedUiSettingValue | null;
17
+ apply(entry: OwnedUiSettingsEntry, value: OwnedUiSettingValue): void;
18
+ /** Saves a structured dialog's record; `rollback` runs when the change fails. */
19
+ saveStructured(entry: OwnedUiSettingsEntry, previous: Readonly<SettingsRecord>, forward: Readonly<SettingsRecord>, rollback: () => void): void;
20
+ undo(): void;
21
+ open(): void;
22
+ /** Drops the history and queued requests; late results no longer record undo steps. */
23
+ close(): void;
24
+ }
25
+ /** The record the engine stores: a choice part at its fallback is unset, so the engine's default applies. */
26
+ export declare function structuredValue(entry: OwnedUiSettingsEntry, record: Readonly<SettingsRecord>): SettingsRecord;
27
+ export declare function sameRecord(left: Readonly<SettingsRecord>, right: Readonly<SettingsRecord>): boolean;
28
+ export declare function replaceRecord(target: SettingsRecord, source: Readonly<SettingsRecord>): void;
29
+ /** Why a change did not take effect, or null when it did. */
30
+ export declare function changeFailure(outcome: OwnedUiSettingsChangeOutcome): string | null;
31
+ export {};
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The settings screen's edits: values shown ahead of the source, and an undo history ordered by edit
3
+ * sequence. Undo requests drain one at a time so each restores against the result of the one before.
4
+ */
5
+ export class SettingsEdits {
6
+ #session;
7
+ #openStructured;
8
+ #pending = new Map();
9
+ #history = [];
10
+ #sequence = 0;
11
+ #requests = 0;
12
+ #active = false;
13
+ #closed = false;
14
+ constructor(session, openStructured) {
15
+ this.#session = session;
16
+ this.#openStructured = openStructured;
17
+ }
18
+ /** The value a row shows: a pending edit, or the source's value. */
19
+ shown(entry) {
20
+ return this.#pending.get(pendingKey(entry)) ?? entry.value;
21
+ }
22
+ apply(entry, value) {
23
+ const key = pendingKey(entry);
24
+ const previous = this.shown(entry);
25
+ const sequence = ++this.#sequence;
26
+ // Invariant: shown immediately so the row never lags a keypress, and so the next press
27
+ // steps from here rather than from a value the source has not caught up to.
28
+ this.#pending.set(key, value);
29
+ void this.#session.change(entry.backend, entry.id, value).then(outcome => {
30
+ const failed = changeFailure(outcome) !== null;
31
+ if (!failed && previous !== null && previous !== value)
32
+ this.#record({ kind: "scalar", sequence, entry, previous });
33
+ // Concurrency: a later press may have moved on; only the last request clears itself.
34
+ if (this.#pending.get(key) === value)
35
+ this.#pending.delete(key);
36
+ });
37
+ }
38
+ /** Saves a structured dialog's record; `rollback` runs when the change fails. */
39
+ saveStructured(entry, previous, forward, rollback) {
40
+ const sequence = ++this.#sequence;
41
+ void this.#session.changeStructured(entry.backend, entry.id, structuredValue(entry, forward)).then(outcome => {
42
+ if (changeFailure(outcome) === null)
43
+ this.#record({ kind: "structured", sequence, entry, previous, forward });
44
+ else
45
+ rollback();
46
+ });
47
+ }
48
+ undo() {
49
+ this.#requests++;
50
+ this.#drain();
51
+ }
52
+ open() { this.#closed = false; }
53
+ /** Drops the history and queued requests; late results no longer record undo steps. */
54
+ close() {
55
+ this.#closed = true;
56
+ this.#history.length = 0;
57
+ this.#requests = 0;
58
+ }
59
+ #record(record) {
60
+ if (this.#closed)
61
+ return;
62
+ const after = this.#history.findIndex(candidate => candidate.sequence > record.sequence);
63
+ if (after < 0)
64
+ this.#history.push(record);
65
+ else
66
+ this.#history.splice(after, 0, record);
67
+ }
68
+ #drain() {
69
+ if (this.#active || this.#requests === 0)
70
+ return;
71
+ const record = this.#history.pop();
72
+ if (record === undefined) {
73
+ this.#requests = 0;
74
+ return;
75
+ }
76
+ this.#requests--;
77
+ this.#active = true;
78
+ if (record.kind === "scalar") {
79
+ const key = pendingKey(record.entry);
80
+ this.#pending.set(key, record.previous);
81
+ void this.#session.change(record.entry.backend, record.entry.id, record.previous).then(outcome => {
82
+ if (this.#pending.get(key) === record.previous)
83
+ this.#pending.delete(key);
84
+ this.#finish(record, outcome);
85
+ });
86
+ return;
87
+ }
88
+ const open = this.#openStructured();
89
+ const target = open !== null && sameEntry(open.entry, record.entry) ? open : null;
90
+ if (target !== null)
91
+ replaceRecord(target.record, record.previous);
92
+ void this.#session.changeStructured(record.entry.backend, record.entry.id, structuredValue(record.entry, record.previous)).then(outcome => {
93
+ if (changeFailure(outcome) !== null && target !== null && this.#openStructured() === target && sameRecord(target.record, record.previous)) {
94
+ replaceRecord(target.record, record.forward);
95
+ }
96
+ this.#finish(record, outcome);
97
+ });
98
+ }
99
+ #finish(record, outcome) {
100
+ if (changeFailure(outcome) !== null)
101
+ this.#record(record);
102
+ this.#active = false;
103
+ this.#drain();
104
+ }
105
+ }
106
+ function pendingKey(entry) {
107
+ return `${entry.backend}:${entry.id}`;
108
+ }
109
+ /** The record the engine stores: a choice part at its fallback is unset, so the engine's default applies. */
110
+ export function structuredValue(entry, record) {
111
+ return Object.fromEntries(Object.entries(record).filter(([key, value]) => {
112
+ const part = entry.flags.find(candidate => candidate.key === key);
113
+ return part?.choices === undefined || value !== part.fallback;
114
+ }));
115
+ }
116
+ export function sameRecord(left, right) {
117
+ const leftEntries = Object.entries(left);
118
+ const rightEntries = Object.entries(right);
119
+ return leftEntries.length === rightEntries.length
120
+ && leftEntries.every(([key, value]) => right[key] === value);
121
+ }
122
+ export function replaceRecord(target, source) {
123
+ for (const key of Object.keys(target))
124
+ delete target[key];
125
+ Object.assign(target, source);
126
+ }
127
+ /** Why a change did not take effect, or null when it did. */
128
+ export function changeFailure(outcome) {
129
+ if (outcome.failure !== null)
130
+ return outcome.failure;
131
+ if (outcome.status === "failed")
132
+ return "the effect failed";
133
+ if (outcome.status === "unavailable")
134
+ return outcome.limitationReason ?? "the setting is unavailable";
135
+ return outcome.limitationReason;
136
+ }
137
+ function sameEntry(left, right) {
138
+ return left.backend === right.backend && left.id === right.id;
139
+ }
@@ -0,0 +1,29 @@
1
+ import { type PaneMouseEvent, type RailPosition, type ScrollbarGeometry, type ScrollbarPresentation, type ScrollbarPresentationInput } from "../../ui/components/index.js";
2
+ /** Where the rail was painted in the last frame, with the list page it scrolls by. */
3
+ export interface SettingsRailFrame {
4
+ readonly rail: RailPosition;
5
+ readonly geometry: ScrollbarGeometry;
6
+ readonly page: number;
7
+ }
8
+ /** How a pointer report landed on the rail: owned keeps it from the list, and `scroll` is the list's new offset. */
9
+ export interface SettingsRailPointer {
10
+ readonly owned: boolean;
11
+ readonly changed: boolean;
12
+ readonly overRail: boolean;
13
+ readonly scroll?: number;
14
+ }
15
+ /** The settings list's scrollbar rail: hover, thumb drag, track paging, and the lingering highlight after a scroll. */
16
+ export declare class SettingsScrollRail {
17
+ #private;
18
+ /** Notes the frame's scroll; a moved list keeps the rail lit and repaints once the highlight lapses. */
19
+ noteRenderedScroll(scroll: number, now: number, requestRender: () => void): void;
20
+ presentation(input: Omit<ScrollbarPresentationInput, "hovered" | "dragging" | "activeUntil">): ScrollbarPresentation;
21
+ /** Records where the rail was painted, or null when this frame has no rail. */
22
+ setFrame(frame: Omit<SettingsRailFrame, "rail"> & {
23
+ readonly rail: Omit<RailPosition, "key">;
24
+ } | null): void;
25
+ pointer(event: PaneMouseEvent, scroll: number): SettingsRailPointer;
26
+ /** Drops hover and any drag, as when a dialog takes the screen. */
27
+ clear(): void;
28
+ dispose(): void;
29
+ }