@termix-ssh/plugin-sdk 1.0.0

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 (236) hide show
  1. package/README.md +52 -0
  2. package/cli/commands/build.mjs +246 -0
  3. package/cli/commands/migrations.mjs +248 -0
  4. package/cli/commands/pack.mjs +42 -0
  5. package/cli/commands/sign.mjs +83 -0
  6. package/cli/commands/test.mjs +33 -0
  7. package/cli/commands/validate.mjs +215 -0
  8. package/cli/index.mjs +65 -0
  9. package/cli/lib/changelog.mjs +126 -0
  10. package/cli/lib/externals.mjs +46 -0
  11. package/cli/lib/plugin-dir.mjs +29 -0
  12. package/cli/lib/signing.mjs +86 -0
  13. package/cli/lib/static-url-imports.mjs +54 -0
  14. package/cli/lib/tmxplug.mjs +106 -0
  15. package/cli/patches/guacamole-common-js-flush.cjs +76 -0
  16. package/cli/patches/xterm-android-ime.cjs +130 -0
  17. package/client.d.ts +13 -0
  18. package/dist/backend.d.ts +1777 -0
  19. package/dist/backend.js +84 -0
  20. package/dist/capabilities.d.ts +38 -0
  21. package/dist/capabilities.js +71 -0
  22. package/dist/db.d.ts +112 -0
  23. package/dist/db.js +189 -0
  24. package/dist/ddl.d.ts +76 -0
  25. package/dist/ddl.js +678 -0
  26. package/dist/desktop.d.ts +346 -0
  27. package/dist/desktop.js +5 -0
  28. package/dist/frontend.d.ts +1206 -0
  29. package/dist/frontend.js +388 -0
  30. package/dist/host/chunk-F3SDZDDG.js +6017 -0
  31. package/dist/host/chunk-XGWE7BTJ.js +90 -0
  32. package/dist/host/chunk-XH5NXL6Y.js +56314 -0
  33. package/dist/host/chunk-YC2MAKSP.js +14 -0
  34. package/dist/host/linked-server-FZGWQW32.js +12 -0
  35. package/dist/host/rbac-api-6KDMBVDZ.js +52 -0
  36. package/dist/host/testing-host.js +414 -0
  37. package/dist/host/theme.css +91 -0
  38. package/dist/host/types/src/types/auth-protocols.d.ts +10 -0
  39. package/dist/host/types/src/types/connection-log.d.ts +13 -0
  40. package/dist/host/types/src/types/credential-sidebar-preferences.d.ts +38 -0
  41. package/dist/host/types/src/types/host-defaults.d.ts +60 -0
  42. package/dist/host/types/src/types/host-sidebar-preferences.d.ts +55 -0
  43. package/dist/host/types/src/types/index.d.ts +298 -0
  44. package/dist/host/types/src/types/keybindings.d.ts +27 -0
  45. package/dist/host/types/src/types/ui-preferences.d.ts +102 -0
  46. package/dist/host/types/src/types/ui-types.d.ts +260 -0
  47. package/dist/host/types/src/ui/api/admin-user-data-api.d.ts +11 -0
  48. package/dist/host/types/src/ui/api/auth-methods-api.d.ts +63 -0
  49. package/dist/host/types/src/ui/api/credential-sidebar-preferences-api.d.ts +3 -0
  50. package/dist/host/types/src/ui/api/credentials-api.d.ts +56 -0
  51. package/dist/host/types/src/ui/api/dashboard-api.d.ts +23 -0
  52. package/dist/host/types/src/ui/api/external-account-api.d.ts +8 -0
  53. package/dist/host/types/src/ui/api/host-sidebar-preferences-api.d.ts +3 -0
  54. package/dist/host/types/src/ui/api/host-status-api.d.ts +11 -0
  55. package/dist/host/types/src/ui/api/open-tabs-api.d.ts +74 -0
  56. package/dist/host/types/src/ui/api/plugins-api.d.ts +128 -0
  57. package/dist/host/types/src/ui/api/rbac-api.d.ts +167 -0
  58. package/dist/host/types/src/ui/api/settings-api.d.ts +22 -0
  59. package/dist/host/types/src/ui/api/ssh-host-management-api.d.ts +65 -0
  60. package/dist/host/types/src/ui/api/sync-api.d.ts +93 -0
  61. package/dist/host/types/src/ui/api/system-status-api.d.ts +49 -0
  62. package/dist/host/types/src/ui/api/ui-preferences-api.d.ts +8 -0
  63. package/dist/host/types/src/ui/api/user-management-api.d.ts +80 -0
  64. package/dist/host/types/src/ui/auth/Auth.d.ts +12 -0
  65. package/dist/host/types/src/ui/auth/auth-bits.d.ts +11 -0
  66. package/dist/host/types/src/ui/auth/silent-signin.d.ts +2 -0
  67. package/dist/host/types/src/ui/components/FullScreenAppWrapper.d.ts +9 -0
  68. package/dist/host/types/src/ui/components/alert-dialog.d.ts +14 -0
  69. package/dist/host/types/src/ui/components/alert.d.ts +9 -0
  70. package/dist/host/types/src/ui/components/badge.d.ts +9 -0
  71. package/dist/host/types/src/ui/components/button.d.ts +13 -0
  72. package/dist/host/types/src/ui/components/card-grid/CardGridCanvas.d.ts +22 -0
  73. package/dist/host/types/src/ui/components/card-grid/layout-utils.d.ts +26 -0
  74. package/dist/host/types/src/ui/components/card-grid/types.d.ts +29 -0
  75. package/dist/host/types/src/ui/components/card-masonry.d.ts +16 -0
  76. package/dist/host/types/src/ui/components/card.d.ts +11 -0
  77. package/dist/host/types/src/ui/components/charts/BarSeries.d.ts +16 -0
  78. package/dist/host/types/src/ui/components/charts/LineChart.d.ts +17 -0
  79. package/dist/host/types/src/ui/components/charts/RadialGauge.d.ts +18 -0
  80. package/dist/host/types/src/ui/components/charts/Sparkline.d.ts +22 -0
  81. package/dist/host/types/src/ui/components/charts/StatRow.d.ts +15 -0
  82. package/dist/host/types/src/ui/components/charts/geometry.d.ts +20 -0
  83. package/dist/host/types/src/ui/components/charts/index.d.ts +4 -0
  84. package/dist/host/types/src/ui/components/checkbox.d.ts +4 -0
  85. package/dist/host/types/src/ui/components/connection/ConnectionGate.d.ts +56 -0
  86. package/dist/host/types/src/ui/components/connection/ConnectionLogPanel.d.ts +9 -0
  87. package/dist/host/types/src/ui/components/connection/ConnectionScreen.d.ts +34 -0
  88. package/dist/host/types/src/ui/components/connection/connection-status.d.ts +1 -0
  89. package/dist/host/types/src/ui/components/data-view.d.ts +32 -0
  90. package/dist/host/types/src/ui/components/dialog.d.ts +17 -0
  91. package/dist/host/types/src/ui/components/dropdown-menu.d.ts +29 -0
  92. package/dist/host/types/src/ui/components/empty-state.d.ts +19 -0
  93. package/dist/host/types/src/ui/components/form-fields.d.ts +115 -0
  94. package/dist/host/types/src/ui/components/input.d.ts +3 -0
  95. package/dist/host/types/src/ui/components/kbd.d.ts +12 -0
  96. package/dist/host/types/src/ui/components/label.d.ts +4 -0
  97. package/dist/host/types/src/ui/components/manager-card.d.ts +25 -0
  98. package/dist/host/types/src/ui/components/meter.d.ts +14 -0
  99. package/dist/host/types/src/ui/components/metric-card.d.ts +18 -0
  100. package/dist/host/types/src/ui/components/panel-layout.d.ts +83 -0
  101. package/dist/host/types/src/ui/components/password-input.d.ts +4 -0
  102. package/dist/host/types/src/ui/components/popover.d.ts +7 -0
  103. package/dist/host/types/src/ui/components/scroll-area.d.ts +5 -0
  104. package/dist/host/types/src/ui/components/section-card.d.ts +29 -0
  105. package/dist/host/types/src/ui/components/select.d.ts +15 -0
  106. package/dist/host/types/src/ui/components/select2.d.ts +8 -0
  107. package/dist/host/types/src/ui/components/separator.d.ts +4 -0
  108. package/dist/host/types/src/ui/components/sheet.d.ts +14 -0
  109. package/dist/host/types/src/ui/components/sidebar.d.ts +69 -0
  110. package/dist/host/types/src/ui/components/skeleton.d.ts +2 -0
  111. package/dist/host/types/src/ui/components/slider.d.ts +4 -0
  112. package/dist/host/types/src/ui/components/sonner.d.ts +4 -0
  113. package/dist/host/types/src/ui/components/surface/prompt-styles.d.ts +4 -0
  114. package/dist/host/types/src/ui/components/surface/surface-scope.d.ts +79 -0
  115. package/dist/host/types/src/ui/components/switch.d.ts +4 -0
  116. package/dist/host/types/src/ui/components/tab-strip.d.ts +19 -0
  117. package/dist/host/types/src/ui/components/tabs.d.ts +7 -0
  118. package/dist/host/types/src/ui/components/textarea.d.ts +4 -0
  119. package/dist/host/types/src/ui/components/theme-provider.d.ts +14 -0
  120. package/dist/host/types/src/ui/components/tooltip.d.ts +7 -0
  121. package/dist/host/types/src/ui/contexts/BrandingContext.d.ts +15 -0
  122. package/dist/host/types/src/ui/contexts/UiPreferencesContext.d.ts +38 -0
  123. package/dist/host/types/src/ui/contexts/branding-document.d.ts +10 -0
  124. package/dist/host/types/src/ui/dashboard/dashboard-cards-registry.d.ts +30 -0
  125. package/dist/host/types/src/ui/hooks/use-action-slot.d.ts +10 -0
  126. package/dist/host/types/src/ui/hooks/use-adaptive-polling.d.ts +5 -0
  127. package/dist/host/types/src/ui/hooks/use-mobile.d.ts +1 -0
  128. package/dist/host/types/src/ui/hooks/use-permissions.d.ts +29 -0
  129. package/dist/host/types/src/ui/hooks/use-status-color-scheme.d.ts +12 -0
  130. package/dist/host/types/src/ui/hooks/use-sync-status.d.ts +4 -0
  131. package/dist/host/types/src/ui/hooks/useSshAuthProviders.d.ts +17 -0
  132. package/dist/host/types/src/ui/i18n/i18n.d.ts +52 -0
  133. package/dist/host/types/src/ui/lib/ServerStatusContext.d.ts +33 -0
  134. package/dist/host/types/src/ui/lib/adaptive-polling.d.ts +29 -0
  135. package/dist/host/types/src/ui/lib/adaptive-resource-budget.d.ts +39 -0
  136. package/dist/host/types/src/ui/lib/app-keyboard-shortcuts.d.ts +8 -0
  137. package/dist/host/types/src/ui/lib/base-path.d.ts +1 -0
  138. package/dist/host/types/src/ui/lib/clipboard-provider.d.ts +9 -0
  139. package/dist/host/types/src/ui/lib/clipboard.d.ts +20 -0
  140. package/dist/host/types/src/ui/lib/connection-origin.d.ts +47 -0
  141. package/dist/host/types/src/ui/lib/db-health-monitor.d.ts +34 -0
  142. package/dist/host/types/src/ui/lib/device-id.d.ts +1 -0
  143. package/dist/host/types/src/ui/lib/electron.d.ts +1 -0
  144. package/dist/host/types/src/ui/lib/embedded-server-status.d.ts +15 -0
  145. package/dist/host/types/src/ui/lib/error-message.d.ts +9 -0
  146. package/dist/host/types/src/ui/lib/frontend-logger.d.ts +67 -0
  147. package/dist/host/types/src/ui/lib/global-shortcut-handler.d.ts +3 -0
  148. package/dist/host/types/src/ui/lib/host-defaults-context.d.ts +48 -0
  149. package/dist/host/types/src/ui/lib/hosts-request-cache.d.ts +9 -0
  150. package/dist/host/types/src/ui/lib/keybinding-match.d.ts +3 -0
  151. package/dist/host/types/src/ui/lib/linked-server.d.ts +10 -0
  152. package/dist/host/types/src/ui/lib/local-adaptive-engine.d.ts +40 -0
  153. package/dist/host/types/src/ui/lib/permissions.d.ts +16 -0
  154. package/dist/host/types/src/ui/lib/plugin-i18n.d.ts +11 -0
  155. package/dist/host/types/src/ui/lib/plugin-icon.d.ts +11 -0
  156. package/dist/host/types/src/ui/lib/plugin-transport.d.ts +50 -0
  157. package/dist/host/types/src/ui/lib/registry.d.ts +29 -0
  158. package/dist/host/types/src/ui/lib/rem.d.ts +7 -0
  159. package/dist/host/types/src/ui/lib/remote-server-api.d.ts +11 -0
  160. package/dist/host/types/src/ui/lib/server-status-store.d.ts +37 -0
  161. package/dist/host/types/src/ui/lib/sync-events.d.ts +2 -0
  162. package/dist/host/types/src/ui/lib/sync-status.d.ts +6 -0
  163. package/dist/host/types/src/ui/lib/tab-jump-hotkey.d.ts +4 -0
  164. package/dist/host/types/src/ui/lib/theme.d.ts +21 -0
  165. package/dist/host/types/src/ui/lib/ttl-request-cache.d.ts +10 -0
  166. package/dist/host/types/src/ui/lib/usage-color.d.ts +5 -0
  167. package/dist/host/types/src/ui/lib/utils.d.ts +2 -0
  168. package/dist/host/types/src/ui/lib/visible-interval.d.ts +5 -0
  169. package/dist/host/types/src/ui/lib/widget-title.d.ts +8 -0
  170. package/dist/host/types/src/ui/lib/ws-auth.d.ts +1 -0
  171. package/dist/host/types/src/ui/main-axios.d.ts +170 -0
  172. package/dist/host/types/src/ui/plugin-host/PluginViewPlaceholder.d.ts +13 -0
  173. package/dist/host/types/src/ui/plugin-host/app.d.ts +18 -0
  174. package/dist/host/types/src/ui/plugin-host/auth-registry.d.ts +50 -0
  175. package/dist/host/types/src/ui/plugin-host/bridge.d.ts +26 -0
  176. package/dist/host/types/src/ui/plugin-host/component-registry.d.ts +24 -0
  177. package/dist/host/types/src/ui/plugin-host/desktop.d.ts +7 -0
  178. package/dist/host/types/src/ui/plugin-host/extension-registry.d.ts +16 -0
  179. package/dist/host/types/src/ui/plugin-host/host-scope.d.ts +26 -0
  180. package/dist/host/types/src/ui/plugin-host/loader.d.ts +52 -0
  181. package/dist/host/types/src/ui/plugin-host/plugin-store.d.ts +40 -0
  182. package/dist/host/types/src/ui/plugin-host/scope.d.ts +20 -0
  183. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +99 -0
  184. package/dist/host/types/src/ui/plugin-host/shell-bridge.d.ts +67 -0
  185. package/dist/host/types/src/ui/plugin-host/view-ownership.d.ts +31 -0
  186. package/dist/host/types/src/ui/settings/HostPluginSections.d.ts +31 -0
  187. package/dist/host/types/src/ui/settings/SettingsFields.d.ts +22 -0
  188. package/dist/host/types/src/ui/settings/host-feature-tabs.d.ts +11 -0
  189. package/dist/host/types/src/ui/settings/settings-components.d.ts +32 -0
  190. package/dist/host/types/src/ui/settings/settings-fields-util.d.ts +11 -0
  191. package/dist/host/types/src/ui/shell/ActionSlot.d.ts +41 -0
  192. package/dist/host/types/src/ui/shell/TabContext.d.ts +30 -0
  193. package/dist/host/types/src/ui/shell/action-registry.d.ts +99 -0
  194. package/dist/host/types/src/ui/shell/keybinding-registry.d.ts +62 -0
  195. package/dist/host/types/src/ui/shell/palette-registry.d.ts +55 -0
  196. package/dist/host/types/src/ui/shell/panel-registry.d.ts +24 -0
  197. package/dist/host/types/src/ui/shell/tab-registry.d.ts +92 -0
  198. package/dist/host/types/src/ui/sidebar/HostManagerTabs.d.ts +65 -0
  199. package/dist/host/types/src/ui/sidebar/host-contributions.d.ts +72 -0
  200. package/dist/host/types/src/ui/sidebar/host-defaults/catalog.d.ts +17 -0
  201. package/dist/host/types/src/ui/sidebar/host-protocols.d.ts +49 -0
  202. package/dist/host/types/src/ui/sidebar/quick-connect-host.d.ts +21 -0
  203. package/dist/host/types/src/ui/sidebar/rail-items.d.ts +103 -0
  204. package/dist/host/types/src/ui/ssh/connection-log/ConnectionLogContext.d.ts +17 -0
  205. package/dist/host/types/src/ui/ssh/dialogs/BrowserSignInDialog.d.ts +18 -0
  206. package/dist/host/types/src/ui/ssh/dialogs/HostKeyVerificationDialog.d.ts +17 -0
  207. package/dist/host/types/src/ui/ssh/dialogs/PassphraseDialog.d.ts +15 -0
  208. package/dist/host/types/src/ui/ssh/dialogs/SSHAuthDialog.d.ts +20 -0
  209. package/dist/host/types/src/ui/ssh/dialogs/TOTPDialog.d.ts +13 -0
  210. package/dist/host/ui.d.ts +1 -0
  211. package/dist/host/ui.js +31754 -0
  212. package/dist/host/use-permissions-NULZL43J.js +18 -0
  213. package/dist/host-commands.d.ts +89 -0
  214. package/dist/host-commands.js +387 -0
  215. package/dist/manifest.d.ts +461 -0
  216. package/dist/manifest.js +1499 -0
  217. package/dist/settings.d.ts +43 -0
  218. package/dist/settings.js +168 -0
  219. package/dist/ssh-certs.d.ts +25 -0
  220. package/dist/ssh-certs.js +234 -0
  221. package/dist/ssh-options.d.ts +23 -0
  222. package/dist/ssh-options.js +1 -0
  223. package/dist/table-builder.d.ts +26 -0
  224. package/dist/table-builder.js +87 -0
  225. package/dist/testing/safe-ports.d.ts +10 -0
  226. package/dist/testing/safe-ports.js +59 -0
  227. package/dist/testing/setup.d.ts +7 -0
  228. package/dist/testing/setup.js +146 -0
  229. package/dist/testing.d.ts +537 -0
  230. package/dist/testing.js +1451 -0
  231. package/dist/vitest-preset.d.ts +54 -0
  232. package/dist/vitest-preset.js +96 -0
  233. package/package.json +143 -0
  234. package/schema/changelog.schema.json +67 -0
  235. package/schema/manifest.schema.json +1160 -0
  236. package/tsconfig.plugin.json +31 -0
@@ -0,0 +1,106 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import zlib from "node:zlib";
4
+
5
+ /** What a .tmxplug holds, in the order the spec lists it. */
6
+ export const TMXPLUG_CONTENTS = [
7
+ "manifest.json",
8
+ "dist",
9
+ "locales",
10
+ "migrations",
11
+ "README.md",
12
+ "CHANGELOG.json",
13
+ "icon.svg",
14
+ ];
15
+
16
+ const BLOCK = 512;
17
+
18
+ /**
19
+ * Every file under the given top-level entries, as sorted posix paths.
20
+ * Directories are implied by their files, so none get their own entry.
21
+ */
22
+ export function collectFiles(cwd, entries = TMXPLUG_CONTENTS) {
23
+ const files = [];
24
+ const walk = (rel) => {
25
+ const full = path.join(cwd, rel);
26
+ const stat = fs.lstatSync(full);
27
+ if (stat.isSymbolicLink()) {
28
+ throw new Error(`${rel} is a symlink; a .tmxplug holds plain files only`);
29
+ }
30
+ if (stat.isDirectory()) {
31
+ for (const child of fs.readdirSync(full)) {
32
+ walk(path.posix.join(rel, child));
33
+ }
34
+ } else if (stat.isFile()) {
35
+ files.push(rel);
36
+ }
37
+ };
38
+ for (const entry of entries) {
39
+ if (fs.existsSync(path.join(cwd, entry))) walk(entry);
40
+ }
41
+ return files.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
42
+ }
43
+
44
+ function writeString(buf, offset, length, value) {
45
+ const bytes = Buffer.from(value, "utf8");
46
+ if (bytes.length > length) throw new Error(`"${value}" is too long`);
47
+ bytes.copy(buf, offset);
48
+ }
49
+
50
+ function writeOctal(buf, offset, length, value) {
51
+ writeString(buf, offset, length, value.toString(8).padStart(length - 1, "0"));
52
+ }
53
+
54
+ function splitName(name) {
55
+ if (Buffer.byteLength(name) <= 100) return { name, prefix: "" };
56
+ for (let i = name.length - 1; i > 0; i--) {
57
+ if (name[i] !== "/") continue;
58
+ const prefix = name.slice(0, i);
59
+ const rest = name.slice(i + 1);
60
+ if (Buffer.byteLength(prefix) <= 155 && Buffer.byteLength(rest) <= 100) {
61
+ return { name: rest, prefix };
62
+ }
63
+ }
64
+ throw new Error(`${name} is too long for a .tmxplug entry`);
65
+ }
66
+
67
+ function header(name, size) {
68
+ const buf = Buffer.alloc(BLOCK);
69
+ const split = splitName(name);
70
+ writeString(buf, 0, 100, split.name);
71
+ writeOctal(buf, 100, 8, 0o644);
72
+ writeOctal(buf, 108, 8, 0);
73
+ writeOctal(buf, 116, 8, 0);
74
+ writeOctal(buf, 124, 12, size);
75
+ writeOctal(buf, 136, 12, 0);
76
+ buf.fill(0x20, 148, 156);
77
+ buf[156] = 0x30;
78
+ writeString(buf, 257, 6, "ustar");
79
+ writeString(buf, 263, 2, "00");
80
+ writeString(buf, 345, 155, split.prefix);
81
+ let sum = 0;
82
+ for (const byte of buf) sum += byte;
83
+ writeString(buf, 148, 8, sum.toString(8).padStart(6, "0") + "\0 ");
84
+ return buf;
85
+ }
86
+
87
+ /**
88
+ * A gzipped ustar archive that depends only on file names and contents:
89
+ * fixed mode, owner and mtime, sorted entries, and a gzip header with no
90
+ * timestamp and a fixed OS byte (Node on Windows writes a different one).
91
+ * Rebuilding from the same tag gives the same sha256 on any machine.
92
+ */
93
+ export function createTmxplug(cwd, files) {
94
+ const parts = [];
95
+ for (const rel of files) {
96
+ const data = fs.readFileSync(path.join(cwd, rel));
97
+ parts.push(header(rel, data.length), data);
98
+ const pad = (BLOCK - (data.length % BLOCK)) % BLOCK;
99
+ if (pad) parts.push(Buffer.alloc(pad));
100
+ }
101
+ parts.push(Buffer.alloc(BLOCK * 2));
102
+ const gz = zlib.gzipSync(Buffer.concat(parts), { level: 9 });
103
+ gz.writeUInt32LE(0, 4);
104
+ gz[9] = 0x03;
105
+ return gz;
106
+ }
@@ -0,0 +1,76 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ // Run by `termix-plugin build` with TERMIX_PATCH_ROOT set to the plugin, so
5
+ // the package resolves the way the plugin's bundle will resolve it.
6
+ function packageDir(name) {
7
+ const root = process.env.TERMIX_PATCH_ROOT || process.cwd();
8
+ // Walked by hand: a package whose exports map hides package.json cannot be
9
+ // found with require.resolve.
10
+ for (let dir = path.resolve(root); ; dir = path.dirname(dir)) {
11
+ const candidate = path.join(dir, "node_modules", name);
12
+ if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
13
+ if (path.dirname(dir) === dir) return path.join(root, "node_modules", name);
14
+ }
15
+ }
16
+
17
+ const packageRoot = path.join(packageDir("guacamole-common-js"));
18
+
19
+ const bundlePaths = [
20
+ path.join(packageRoot, "dist", "esm", "guacamole-common.js"),
21
+ path.join(packageRoot, "dist", "cjs", "guacamole-common.js"),
22
+ ];
23
+
24
+ const oldFlushBlock =
25
+ " if (window.requestAnimationFrame && document.hasFocus())\n" +
26
+ " asyncFlush();\n" +
27
+ " else\n" +
28
+ " syncFlush();";
29
+
30
+ const newFlushBlock =
31
+ " // Electron can throttle or skip requestAnimationFrame() for inactive\n" +
32
+ " // windows/tabs even while guacd is still sending display frames. Flush\n" +
33
+ " // synchronously so Guacamole connections do not stall while waiting for\n" +
34
+ " // a frame callback that may never run.\n" +
35
+ " syncFlush();";
36
+
37
+ let patched = false;
38
+ let foundBundle = false;
39
+
40
+ for (const bundlePath of bundlePaths) {
41
+ if (!fs.existsSync(bundlePath)) {
42
+ console.log(
43
+ `[patch-guacamole-common-js] ${bundlePath} not found, skipping`,
44
+ );
45
+ continue;
46
+ }
47
+
48
+ foundBundle = true;
49
+ let content = fs.readFileSync(bundlePath, "utf8");
50
+ if (content.includes(newFlushBlock)) continue;
51
+
52
+ if (!content.includes(oldFlushBlock)) {
53
+ console.log(
54
+ `[patch-guacamole-common-js] Flush target not found in ${bundlePath}, skipping`,
55
+ );
56
+ continue;
57
+ }
58
+
59
+ content = content.replace(oldFlushBlock, newFlushBlock);
60
+ fs.writeFileSync(bundlePath, content);
61
+ patched = true;
62
+ }
63
+
64
+ if (!foundBundle) {
65
+ console.log("[patch-guacamole-common-js] File not found, skipping");
66
+ process.exit(0);
67
+ }
68
+
69
+ if (!patched) {
70
+ console.log("[patch-guacamole-common-js] Already patched");
71
+ process.exit(0);
72
+ }
73
+
74
+ console.log(
75
+ "[patch-guacamole-common-js] Patched display flush to avoid Electron requestAnimationFrame stalls",
76
+ );
@@ -0,0 +1,130 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ // Run by `termix-plugin build` with TERMIX_PATCH_ROOT set to the plugin, so
5
+ // the package resolves the way the plugin's bundle will resolve it.
6
+ function packageDir(name) {
7
+ const root = process.env.TERMIX_PATCH_ROOT || process.cwd();
8
+ // Walked by hand: a package whose exports map hides package.json cannot be
9
+ // found with require.resolve.
10
+ for (let dir = path.resolve(root); ; dir = path.dirname(dir)) {
11
+ const candidate = path.join(dir, "node_modules", name);
12
+ if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
13
+ if (path.dirname(dir) === dir) return path.join(root, "node_modules", name);
14
+ }
15
+ }
16
+
17
+ const xtermDir = path.join(packageDir("@xterm/xterm"), "lib");
18
+
19
+ // Backport the textarea-shrink fix from gmuxapp/xterm.js@6a011cf while
20
+ // xtermjs/xterm.js#3600 remains unresolved upstream. Android IMEs can restart
21
+ // composition on the previous word and replace it with a shorter value (for
22
+ // example, Vietnamese "Hoar" -> "Hỏa"). xterm 6.0 otherwise emits nothing.
23
+ //
24
+ // Also fixes _handleAnyTextareaChanges, which iOS Safari/WKWebView drives
25
+ // for ordinary typing (it reports keyCode 229 for all software-keyboard
26
+ // input, not just IME composition). That handler diffs the textarea value
27
+ // via `newValue.replace(oldValue, "")`, a literal substring removal. When
28
+ // keystrokes arrive faster than the function's setTimeout(0) callback runs,
29
+ // several overlapping callbacks each capture a stale oldValue, so the
30
+ // literal-substring search fails to match and the diff silently comes back
31
+ // empty - characters are dropped instead of sent. Swap in the same
32
+ // common-prefix diff used for composition-end above so a stale oldValue
33
+ // still yields the correct delta.
34
+ const patches = [
35
+ {
36
+ file: "xterm.mjs",
37
+ replacements: [
38
+ [
39
+ [
40
+ 'this._compositionPosition={start:0,end:0},this._dataAlreadySent=""',
41
+ 'this._compositionPosition={start:0,end:0},this._preCompositionValue="",this._dataAlreadySent=""',
42
+ ],
43
+ 'this._compositionPosition={start:0,end:0},this._preCompositionValue="",this._pendingTextareaValue=null,this._dataAlreadySent=""',
44
+ ],
45
+ [
46
+ 'this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent=""',
47
+ 'this._compositionPosition.start=this._textarea.value.length,this._preCompositionValue=this._textarea.value,this._compositionView.textContent=""',
48
+ ],
49
+ [
50
+ "let e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0",
51
+ "let e={start:this._compositionPosition.start,end:this._compositionPosition.end};const s=this._preCompositionValue;this._isSendingComposition=!0",
52
+ ],
53
+ [
54
+ "e.start+=this._dataAlreadySent.length,this._isComposing?i=this._textarea.value.substring(e.start,this._compositionPosition.start):i=this._textarea.value.substring(e.start),i.length>0&&",
55
+ "e.start+=this._dataAlreadySent.length;if(this._isComposing)i=this._textarea.value.substring(e.start,this._compositionPosition.start);else{const t=this._textarea.value;if(t.length<s.length){let e=0;const r=Math.min(t.length,s.length);for(;e<r&&t.charCodeAt(e)===s.charCodeAt(e);)e++;i=b.DEL.repeat(s.length-e)+t.substring(e)}else i=t.substring(e.start)}i.length>0&&",
56
+ ],
57
+ [
58
+ [
59
+ '_handleAnyTextareaChanges(){let t=this._textarea.value;setTimeout(()=>{if(!this._isComposing){let e=this._textarea.value,i=e.replace(t,"");this._dataAlreadySent=i,e.length>t.length?this._coreService.triggerDataEvent(i,!0):e.length<t.length?this._coreService.triggerDataEvent(`${b.DEL}`,!0):e.length===t.length&&e!==t&&this._coreService.triggerDataEvent(e,!0)}},0)}',
60
+ "_handleAnyTextareaChanges(){let t=this._textarea.value;setTimeout(()=>{if(!this._isComposing){let e=this._textarea.value,r=0;const n=Math.min(e.length,t.length);for(;r<n&&e.charCodeAt(r)===t.charCodeAt(r);)r++;let i=e.length<t.length?b.DEL.repeat(t.length-r)+e.substring(r):e.substring(r);this._dataAlreadySent=i,i.length>0&&this._coreService.triggerDataEvent(i,!0)}},0)}",
61
+ ],
62
+ "_handleAnyTextareaChanges(){if(this._pendingTextareaValue!==null)return;this._pendingTextareaValue=this._textarea.value,setTimeout(()=>{const t=this._pendingTextareaValue;this._pendingTextareaValue=null;if(!this._isComposing){let e=this._textarea.value,r=0;const n=Math.min(e.length,t.length);for(;r<n&&e.charCodeAt(r)===t.charCodeAt(r);)r++;let i=e.length<t.length?b.DEL.repeat(t.length-r)+e.substring(r):e.substring(r);this._dataAlreadySent=i,i.length>0&&this._coreService.triggerDataEvent(i,!0)}},0)}",
63
+ ],
64
+ ],
65
+ },
66
+ {
67
+ file: "xterm.js",
68
+ replacements: [
69
+ [
70
+ [
71
+ 'this._compositionPosition={start:0,end:0},this._dataAlreadySent=""',
72
+ 'this._compositionPosition={start:0,end:0},this._preCompositionValue="",this._dataAlreadySent=""',
73
+ ],
74
+ 'this._compositionPosition={start:0,end:0},this._preCompositionValue="",this._pendingTextareaValue=null,this._dataAlreadySent=""',
75
+ ],
76
+ [
77
+ 'this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent=""',
78
+ 'this._compositionPosition.start=this._textarea.value.length,this._preCompositionValue=this._textarea.value,this._compositionView.textContent=""',
79
+ ],
80
+ [
81
+ "const e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0",
82
+ "const e={start:this._compositionPosition.start,end:this._compositionPosition.end},i=this._preCompositionValue;this._isSendingComposition=!0",
83
+ ],
84
+ [
85
+ "e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,this._compositionPosition.start):this._textarea.value.substring(e.start),t.length>0&&",
86
+ "e.start+=this._dataAlreadySent.length;this._isComposing?t=this._textarea.value.substring(e.start,this._compositionPosition.start):(()=>{const s=this._textarea.value;if(s.length<i.length){let e=0;const r=Math.min(s.length,i.length);for(;e<r&&s.charCodeAt(e)===i.charCodeAt(e);)e++;t=a.C0.DEL.repeat(i.length-e)+s.substring(e)}else t=s.substring(e.start)})(),t.length>0&&",
87
+ ],
88
+ [
89
+ [
90
+ '_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.length<e.length?this._coreService.triggerDataEvent(`${a.C0.DEL}`,!0):t.length===e.length&&t!==e&&this._coreService.triggerDataEvent(t,!0)}}),0)}',
91
+ "_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value;let r=0;const n=Math.min(t.length,e.length);for(;r<n&&t.charCodeAt(r)===e.charCodeAt(r);)r++;const i=t.length<e.length?a.C0.DEL.repeat(e.length-r)+t.substring(r):t.substring(r);this._dataAlreadySent=i,i.length>0&&this._coreService.triggerDataEvent(i,!0)}}),0)}",
92
+ ],
93
+ "_handleAnyTextareaChanges(){if(this._pendingTextareaValue!==null)return;this._pendingTextareaValue=this._textarea.value,setTimeout((()=>{const e=this._pendingTextareaValue;this._pendingTextareaValue=null;if(!this._isComposing){const t=this._textarea.value;let r=0;const n=Math.min(t.length,e.length);for(;r<n&&t.charCodeAt(r)===e.charCodeAt(r);)r++;const i=t.length<e.length?a.C0.DEL.repeat(e.length-r)+t.substring(r):t.substring(r);this._dataAlreadySent=i,i.length>0&&this._coreService.triggerDataEvent(i,!0)}}),0)}",
94
+ ],
95
+ ],
96
+ },
97
+ ];
98
+
99
+ for (const { file, replacements } of patches) {
100
+ const filePath = path.join(xtermDir, file);
101
+ if (!fs.existsSync(filePath)) {
102
+ throw new Error(`[patch-xterm-android-ime] Missing ${filePath}`);
103
+ }
104
+
105
+ let source = fs.readFileSync(filePath, "utf8");
106
+ let changed = false;
107
+
108
+ for (const [original, patched] of replacements) {
109
+ if (source.includes(patched)) {
110
+ continue;
111
+ }
112
+ const originals = Array.isArray(original) ? original : [original];
113
+ const matched = originals.find((candidate) => source.includes(candidate));
114
+ if (!matched) {
115
+ throw new Error(
116
+ `[patch-xterm-android-ime] Expected source not found in ${file}`,
117
+ );
118
+ }
119
+ source = source.replace(matched, patched);
120
+ changed = true;
121
+ }
122
+
123
+ if (!changed) {
124
+ console.log(`[patch-xterm-android-ime] ${file} already patched`);
125
+ continue;
126
+ }
127
+
128
+ fs.writeFileSync(filePath, source);
129
+ console.log(`[patch-xterm-android-ime] Patched ${file}`);
130
+ }
package/client.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Import forms `termix-plugin build` understands beyond plain modules. Picked
3
+ * up through tsconfig.plugin.json.
4
+ */
5
+
6
+ /** A script copied next to the bundle; the import is its URL. */
7
+ declare module "*?url" {
8
+ const url: string;
9
+ export default url;
10
+ }
11
+
12
+ /** A stylesheet imported for its side effect; the build emits frontend.css. */
13
+ declare module "*.css";