@xpufx/paseo-top 0.4.3 → 0.4.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.
Files changed (130) hide show
  1. package/OVERVIEW.md +17 -0
  2. package/client/host-ui.tsx +184 -0
  3. package/client/multi-host-view.tsx +57 -43
  4. package/client/multi-host.ts +36 -13
  5. package/client/pill-labels.ts +124 -7
  6. package/client/pill.tsx +724 -669
  7. package/client/resources-query.ts +1 -1
  8. package/client/settings-events.ts +33 -0
  9. package/client/settings-ui.tsx +86 -9
  10. package/client/surface.tsx +310 -274
  11. package/client/telemetry-copy.ts +8 -2
  12. package/client/telemetry.tsx +372 -321
  13. package/client/turn-panel.tsx +126 -104
  14. package/client/vendor/paseo-plugin-helper/{host.ts → core/host.ts} +21 -61
  15. package/client/vendor/paseo-plugin-helper/core/index.ts +76 -0
  16. package/client/vendor/paseo-plugin-helper/core/plugin-load-count.ts +89 -0
  17. package/client/vendor/paseo-plugin-helper/{query-refresh.ts → core/query-refresh.ts} +2 -2
  18. package/client/vendor/paseo-plugin-helper/{query.ts → core/query.ts} +2 -2
  19. package/client/vendor/paseo-plugin-helper/{settings.ts → core/settings.ts} +1 -1
  20. package/client/vendor/paseo-plugin-helper/{shared-settings.ts → core/shared-settings.ts} +2 -2
  21. package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/clipboard.ts +2 -2
  22. package/client/vendor/paseo-plugin-helper/lifecycle/flair.ts +26 -0
  23. package/client/vendor/paseo-plugin-helper/{theme/color-utils.ts → lifecycle/host-color.ts} +95 -38
  24. package/client/vendor/paseo-plugin-helper/lifecycle/host-theme.tsx +109 -0
  25. package/client/vendor/paseo-plugin-helper/lifecycle/index.ts +23 -0
  26. package/client/vendor/paseo-plugin-helper/lifecycle/multi-host.ts +66 -0
  27. package/client/vendor/paseo-plugin-helper/{panel.tsx → lifecycle/panel.tsx} +7 -7
  28. package/client/vendor/paseo-plugin-helper/{pill.tsx → lifecycle/pill.tsx} +118 -338
  29. package/client/vendor/paseo-plugin-helper/lifecycle/providers.tsx +46 -0
  30. package/client/vendor/paseo-plugin-helper/lifecycle/scroll-owner.ts +26 -0
  31. package/client/vendor/paseo-plugin-helper/lifecycle/surface.tsx +140 -0
  32. package/client/vendor/paseo-plugin-helper/ui/color.ts +1 -0
  33. package/client/vendor/paseo-plugin-helper/ui/content.tsx +1326 -0
  34. package/client/vendor/paseo-plugin-helper/ui/controls.tsx +1018 -0
  35. package/client/vendor/paseo-plugin-helper/{components/DataTable.tsx → ui/data.tsx} +55 -47
  36. package/client/vendor/paseo-plugin-helper/ui/index.ts +44 -0
  37. package/client/vendor/paseo-plugin-helper/ui/layout.tsx +263 -0
  38. package/client/vendor/paseo-plugin-helper/ui/modal.tsx +111 -0
  39. package/client/vendor/paseo-plugin-helper/ui/plugin-reload-banner.tsx +119 -0
  40. package/client/vendor/paseo-plugin-helper/{settings-screen.tsx → ui/settings-screen.tsx} +3 -3
  41. package/client/vendor/paseo-plugin-helper/ui/settings.ts +36 -0
  42. package/client/vendor/paseo-plugin-helper/ui/text.tsx +821 -0
  43. package/client/vendor/paseo-plugin-helper/ui/theme.tsx +1 -0
  44. package/client/vendor/permission-audit/client.tsx +706 -0
  45. package/client/vendor/permission-audit/index.tsx +1 -0
  46. package/index.client.tsx +58 -37
  47. package/index.server.ts +256 -227
  48. package/package.json +14 -2
  49. package/paseo-plugin.json +2 -0
  50. package/screenshots/1-paseo-top-modal-system.jpg +0 -0
  51. package/screenshots/2-paseo-top-modal-workspace.jpg +0 -0
  52. package/screenshots/3-paseo-top-modal-settings.jpg +0 -0
  53. package/screenshots/4-paseo-top-modal-about.jpg +0 -0
  54. package/screenshots/paseo-top-dashboard.jpg +0 -0
  55. package/screenshots/paseo-top-general-view.jpg +0 -0
  56. package/server/resources.ts +98 -0
  57. package/server/vendor/paseo-plugin-helper/custom-pills.ts +70 -0
  58. package/server/vendor/paseo-plugin-helper/index.ts +4 -0
  59. package/server/vendor/paseo-plugin-helper/logger.ts +5 -16
  60. package/server/vendor/paseo-plugin-helper/mcp-injection.ts +20 -2
  61. package/server/vendor/paseo-plugin-helper/plugins.ts +184 -19
  62. package/server/vendor/paseo-plugin-helper/rpc-guard.ts +2 -2
  63. package/server/vendor/paseo-plugin-helper/storage-guard.ts +294 -0
  64. package/server/vendor/paseo-plugin-helper/storage.ts +36 -2
  65. package/server/vendor/paseo-plugin-helper/telemetry.ts +486 -0
  66. package/server/vendor/paseo-plugin-helper/tickets.ts +1232 -0
  67. package/server/vendor/permission-audit/index.ts +1 -0
  68. package/server/vendor/permission-audit/server.ts +839 -0
  69. package/shared/helper-version.ts +20 -0
  70. package/shared/multi-host.ts +156 -155
  71. package/shared/resources.ts +146 -1
  72. package/shared/vendor/paseo-plugin-helper/async.ts +14 -7
  73. package/shared/vendor/paseo-plugin-helper/features.ts +244 -0
  74. package/shared/vendor/paseo-plugin-helper/forge.ts +27 -0
  75. package/shared/vendor/paseo-plugin-helper/index.ts +2 -0
  76. package/shared/vendor/paseo-plugin-helper/tickets.ts +1104 -0
  77. package/shared/vendor/permission-audit/filter.ts +61 -0
  78. package/shared/vendor/permission-audit/index.ts +2 -0
  79. package/shared/vendor/permission-audit/shared.ts +119 -0
  80. package/shared/version.ts +1 -1
  81. package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +0 -493
  82. package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +0 -250
  83. package/client/vendor/paseo-plugin-helper/components/Badge.tsx +0 -156
  84. package/client/vendor/paseo-plugin-helper/components/Button.tsx +0 -184
  85. package/client/vendor/paseo-plugin-helper/components/Card.tsx +0 -225
  86. package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +0 -196
  87. package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +0 -277
  88. package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +0 -172
  89. package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +0 -180
  90. package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +0 -97
  91. package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +0 -70
  92. package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +0 -75
  93. package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +0 -446
  94. package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +0 -247
  95. package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +0 -117
  96. package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +0 -53
  97. package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +0 -118
  98. package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +0 -80
  99. package/client/vendor/paseo-plugin-helper/components/Select.tsx +0 -215
  100. package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +0 -80
  101. package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +0 -319
  102. package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +0 -150
  103. package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +0 -163
  104. package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +0 -157
  105. package/client/vendor/paseo-plugin-helper/components/index.ts +0 -25
  106. package/client/vendor/paseo-plugin-helper/custom-pills.tsx +0 -224
  107. package/client/vendor/paseo-plugin-helper/forge-icon.tsx +0 -79
  108. package/client/vendor/paseo-plugin-helper/icon.tsx +0 -39
  109. package/client/vendor/paseo-plugin-helper/index.ts +0 -29
  110. package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +0 -49
  111. package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +0 -103
  112. package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +0 -65
  113. package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +0 -378
  114. package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +0 -51
  115. package/client/vendor/paseo-plugin-helper/layout/Row.tsx +0 -39
  116. package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +0 -39
  117. package/client/vendor/paseo-plugin-helper/layout/index.ts +0 -7
  118. package/client/vendor/paseo-plugin-helper/styles/index.ts +0 -1
  119. package/client/vendor/paseo-plugin-helper/styles/recipes.ts +0 -550
  120. package/client/vendor/paseo-plugin-helper/surface.tsx +0 -80
  121. package/client/vendor/paseo-plugin-helper/theme/flair.ts +0 -76
  122. package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +0 -121
  123. package/client/vendor/paseo-plugin-helper/theme/index.ts +0 -7
  124. package/client/vendor/paseo-plugin-helper/theme/provider.tsx +0 -178
  125. package/client/vendor/paseo-plugin-helper/theme/responsive.ts +0 -213
  126. package/client/vendor/paseo-plugin-helper/theme/tokens.ts +0 -200
  127. package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +0 -57
  128. /package/client/vendor/paseo-plugin-helper/{command-center.ts → core/command-center.ts} +0 -0
  129. /package/client/vendor/paseo-plugin-helper/{snapshot.ts → core/snapshot.ts} +0 -0
  130. /package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/haptics.ts +0 -0
@@ -0,0 +1,244 @@
1
+ import type { PluginRpcContract } from "./rpc";
2
+
3
+ /**
4
+ * Cleanup handle returned by a feature contribution.
5
+ *
6
+ * A disposer MUST be idempotent: calling it more than once must not throw and
7
+ * must not tear down anything twice. `composeFeatureModules` additionally
8
+ * wraps every disposer it receives so the *composed* teardown is idempotent
9
+ * even if an individual module forgets.
10
+ */
11
+ export type FeatureDisposer = () => void;
12
+
13
+ /**
14
+ * A self-contained, droppable unit of plugin functionality.
15
+ *
16
+ * A feature module owns one concern (custom pills, timeline telemetry,
17
+ * permission auditing, …) and exposes it through up to two contribution hooks:
18
+ * `contributeServer` for the daemon-side `PluginServerContext` and
19
+ * `contributeClient` for the client-side `PluginClientContext`. Both hooks are
20
+ * optional; a module may contribute to one side, both, or neither.
21
+ *
22
+ * Lifecycle contract
23
+ * ------------------
24
+ * - A contribution hook runs at most once per `contribute*` call on the
25
+ * composing plugin entry.
26
+ * - It may return a {@link FeatureDisposer} for cleanup, or `void` when it owns
27
+ * nothing to release.
28
+ * - Returned disposers MUST be idempotent (see {@link FeatureDisposer}).
29
+ * - The plugin entry composes modules with {@link composeFeatureModules} and
30
+ * returns the combined disposer to Paseo.
31
+ * - `contracts` is declarative metadata (name-keyed RPC contracts the module
32
+ * owns); it is not registered automatically. The composing entry still calls
33
+ * `server.handle(contract, handler)`.
34
+ *
35
+ * Context types are generic because `paseo-plugin-helper/shared` must not
36
+ * import the Paseo SDK. Type a module with the SDK contexts at the call site:
37
+ *
38
+ * ```ts
39
+ * import type { PluginClientContext } from "@getpaseo/plugin/client";
40
+ * import type { PluginServerContext } from "@getpaseo/plugin/server";
41
+ * import type { FeatureModule } from "paseo-plugin-helper/shared";
42
+ *
43
+ * export const customPills: FeatureModule<PluginServerContext, PluginClientContext> = {
44
+ * id: "custom-pills",
45
+ * contracts: { list: listCustomPillsRpc },
46
+ * contributeServer(server) {
47
+ * const { unsubscribe } = registerCustomPillsServer(server);
48
+ * return unsubscribe;
49
+ * },
50
+ * contributeClient(client) {
51
+ * const remove = registerCustomPills(client);
52
+ * return remove;
53
+ * },
54
+ * };
55
+ * ```
56
+ */
57
+ export interface FeatureModule<
58
+ TServerContext = unknown,
59
+ TClientContext = unknown,
60
+ > {
61
+ /** Stable, plugin-unique module id (e.g. `"custom-pills"`). */
62
+ readonly id: string;
63
+ /** Daemon-side contribution. Returns an idempotent disposer, or `void`. */
64
+ readonly contributeServer?: (server: TServerContext) => FeatureDisposer | void;
65
+ /** Client-side contribution. Returns an idempotent disposer, or `void`. */
66
+ readonly contributeClient?: (client: TClientContext) => FeatureDisposer | void;
67
+ /** RPC contracts this module owns, keyed by a local name. */
68
+ readonly contracts?: Record<string, PluginRpcContract>;
69
+ }
70
+
71
+ /**
72
+ * A composed set of feature modules. `contributeServer` / `contributeClient`
73
+ * run every module's hook for that side and return a single combined,
74
+ * idempotent disposer that tears the modules down in reverse order.
75
+ */
76
+ export interface FeatureComposition<
77
+ TServerContext = unknown,
78
+ TClientContext = unknown,
79
+ > {
80
+ /** Module ids in composition order. */
81
+ readonly ids: readonly string[];
82
+ /** Every declared contract, flattened in module order and de-duplicated. */
83
+ readonly contracts: readonly PluginRpcContract[];
84
+ /** Run all `contributeServer` hooks; returns the combined disposer. */
85
+ contributeServer(server: TServerContext): FeatureDisposer;
86
+ /** Run all `contributeClient` hooks; returns the combined disposer. */
87
+ contributeClient(client: TClientContext): FeatureDisposer;
88
+ }
89
+
90
+ function toIdempotentDisposer(disposer: FeatureDisposer): FeatureDisposer {
91
+ let called = false;
92
+ return () => {
93
+ if (called) return;
94
+ called = true;
95
+ disposer();
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Run a set of disposers in reverse (LIFO) order. Every disposer runs even if
101
+ * an earlier one throws; the first error is re-thrown after teardown finishes.
102
+ */
103
+ function disposeAll(disposers: readonly FeatureDisposer[]): void {
104
+ let hasError = false;
105
+ let firstError: unknown;
106
+ for (let i = disposers.length - 1; i >= 0; i -= 1) {
107
+ try {
108
+ disposers[i]!();
109
+ } catch (error) {
110
+ if (!hasError) {
111
+ hasError = true;
112
+ firstError = error;
113
+ }
114
+ }
115
+ }
116
+ if (hasError) throw firstError;
117
+ }
118
+
119
+ /**
120
+ * Collect the disposers produced by a per-side contribution hook. If a module
121
+ * throws mid-composition, already-contributed modules are disposed before the
122
+ * error is re-thrown so a failed composition never leaks partial state.
123
+ */
124
+ function runContributions<TServerContext, TClientContext>(
125
+ modules: readonly FeatureModule<TServerContext, TClientContext>[],
126
+ contribute: (
127
+ module: FeatureModule<TServerContext, TClientContext>,
128
+ ) => FeatureDisposer | void,
129
+ ): FeatureDisposer {
130
+ const disposers: FeatureDisposer[] = [];
131
+ let disposed = false;
132
+ const dispose = () => {
133
+ if (disposed) return;
134
+ disposed = true;
135
+ disposeAll(disposers);
136
+ };
137
+
138
+ try {
139
+ for (const module of modules) {
140
+ const result = contribute(module);
141
+ if (typeof result === "function") {
142
+ disposers.push(toIdempotentDisposer(result));
143
+ }
144
+ }
145
+ } catch (error) {
146
+ // Roll back what already mounted, but never let a failing disposer mask
147
+ // the contribution error that triggered the rollback.
148
+ try {
149
+ dispose();
150
+ } catch {
151
+ // ignored
152
+ }
153
+ throw error;
154
+ }
155
+
156
+ return dispose;
157
+ }
158
+
159
+ function assertUniqueModules<TServerContext, TClientContext>(
160
+ modules: readonly FeatureModule<TServerContext, TClientContext>[],
161
+ ): void {
162
+ const seen = new Set<string>();
163
+ for (const module of modules) {
164
+ const id = typeof module.id === "string" ? module.id.trim() : "";
165
+ if (!id) {
166
+ throw new Error("Feature module id must be a non-empty string");
167
+ }
168
+ if (seen.has(id)) {
169
+ throw new Error(`Duplicate feature module id "${id}"`);
170
+ }
171
+ seen.add(id);
172
+ }
173
+ }
174
+
175
+ function collectContracts<TServerContext, TClientContext>(
176
+ modules: readonly FeatureModule<TServerContext, TClientContext>[],
177
+ ): readonly PluginRpcContract[] {
178
+ const contracts: PluginRpcContract[] = [];
179
+ const seen = new Set<string>();
180
+ for (const module of modules) {
181
+ for (const contract of Object.values(module.contracts ?? {})) {
182
+ if (seen.has(contract.name)) {
183
+ throw new Error(
184
+ `Duplicate RPC contract "${contract.name}" across feature modules`,
185
+ );
186
+ }
187
+ seen.add(contract.name);
188
+ contracts.push(contract);
189
+ }
190
+ }
191
+ return Object.freeze(contracts);
192
+ }
193
+
194
+ /**
195
+ * Compose {@link FeatureModule}s into one contribution unit for a plugin entry.
196
+ *
197
+ * Modules run in array order on contribution and are disposed in reverse (LIFO)
198
+ * order. The returned combined disposer is idempotent: calling it twice runs
199
+ * teardown once and never throws the first error a second time.
200
+ *
201
+ * ```ts
202
+ * import type { PluginClientContext } from "@getpaseo/plugin/client";
203
+ * import type { PluginServerContext } from "@getpaseo/plugin/server";
204
+ * import { composeFeatureModules } from "paseo-plugin-helper/shared";
205
+ * import { customPills } from "./features/custom-pills";
206
+ * import { timelineTelemetry } from "./features/timeline-telemetry";
207
+ *
208
+ * const features = composeFeatureModules<PluginServerContext, PluginClientContext>([
209
+ * customPills,
210
+ * timelineTelemetry,
211
+ * ]);
212
+ *
213
+ * export function contributeServer(server: PluginServerContext) {
214
+ * return features.contributeServer(server);
215
+ * }
216
+ *
217
+ * export function contributeClient(client: PluginClientContext) {
218
+ * return features.contributeClient(client);
219
+ * }
220
+ * ```
221
+ *
222
+ * @throws if two modules share an `id` or declare the same RPC contract `name`.
223
+ */
224
+ export function composeFeatureModules<
225
+ TServerContext = unknown,
226
+ TClientContext = unknown,
227
+ >(
228
+ modules: readonly FeatureModule<TServerContext, TClientContext>[],
229
+ ): FeatureComposition<TServerContext, TClientContext> {
230
+ const list = [...modules];
231
+ assertUniqueModules(list);
232
+ const contracts = collectContracts(list);
233
+
234
+ return {
235
+ ids: Object.freeze(list.map((module) => module.id)),
236
+ contracts,
237
+ contributeServer(server) {
238
+ return runContributions(list, (module) => module.contributeServer?.(server));
239
+ },
240
+ contributeClient(client) {
241
+ return runContributions(list, (module) => module.contributeClient?.(client));
242
+ },
243
+ };
244
+ }
@@ -108,3 +108,30 @@ export function resolveForgeMark(
108
108
  const resolved = isForgeKind(explicit) ? explicit : forgeKindFromHost(host);
109
109
  return FORGE_MARKS[resolved];
110
110
  }
111
+
112
+ /**
113
+ * Official monochrome forge marks (Simple Icons, CC0 / project trademarks),
114
+ * authored on a 24x24 viewBox. Rendered as inline SVG through a data URI so a
115
+ * consumer needs no SVG dependency: exact vector marks on web/Electron, where
116
+ * Paseo renders React Native Web, and a Lucide fallback on native.
117
+ *
118
+ * Pure and React-free so it lives in `shared/` next to the resolver.
119
+ */
120
+ const FORGE_MARK_PATHS: Partial<Record<ForgeKind, string>> = {
121
+ codeberg:
122
+ "M11.999.747A11.974 11.974 0 0 0 0 12.75c0 2.254.635 4.465 1.833 6.376L11.837 6.19c.072-.092.251-.092.323 0l4.178 5.402h-2.992l.065.239h3.113l.882 1.138h-3.674l.103.374h3.86l.777 1.003h-4.358l.135.483h4.593l.695.894h-5.038l.165.589h5.326l.609.785h-5.717l.182.65h6.038l.562.727h-6.397l.183.65h6.717A12.003 12.003 0 0 0 24 12.75 11.977 11.977 0 0 0 11.999.747zm3.654 19.104.182.65h5.326c.173-.204.353-.433.513-.65zm.385 1.377.18.65h3.563c.233-.198.485-.428.712-.65zm.383 1.377.182.648h1.203c.356-.204.685-.412 1.042-.648zz",
123
+ forgejo:
124
+ "M16.7773 0c1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.1175a7.0759 7.0759 0 0 1 4.148-1.4205l.1176-.001 1.3385.0002c.4973-.8827 1.4434-1.4788 2.5288-1.4788 1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.319c.8827.4973 1.4788 1.4434 1.4788 2.5287 0 1.602-1.2986 2.9005-2.9005 2.9005-1.6018 0-2.9004-1.2986-2.9004-2.9005 0-1.0853.596-2.0314 1.4788-2.5287l-.0002-9.9831c0-3.887 3.1195-7.0453 6.9915-7.108l.1176-.001h1.3385C14.7458.5962 15.692 0 16.7773 0ZM7.2227 19.9052c-.6596 0-1.1943.5347-1.1943 1.1943s.5347 1.1943 1.1943 1.1943 1.1944-.5347 1.1944-1.1943-.5348-1.1943-1.1944-1.1943Zm9.5546-10.4644c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Zm0-7.7346c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Z",
125
+ gitea:
126
+ "M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z",
127
+ };
128
+
129
+ /** Inline SVG data URI for a custom mark, tinted with the resolved color. */
130
+ export function forgeMarkSource(kind: ForgeKind, color: string): { uri: string } | null {
131
+ const path = FORGE_MARK_PATHS[kind];
132
+ if (!path) return null;
133
+ const svg =
134
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">` +
135
+ `<path fill="${color}" d="${path}"/></svg>`;
136
+ return { uri: `data:image/svg+xml,${encodeURIComponent(svg)}` };
137
+ }
@@ -1,5 +1,6 @@
1
1
  export * from "./types";
2
2
  export * from "./rpc";
3
+ export * from "./features";
3
4
  export * from "./formatters";
4
5
  export * from "./settings";
5
6
  export * from "./suite-settings";
@@ -8,3 +9,4 @@ export * from "./suppressed";
8
9
  export * from "./custom-pills";
9
10
  export * from "./forge";
10
11
  export * from "./highlight";
12
+ export * from "./tickets";