@marimo-team/islands 0.23.15-dev55 → 0.23.15-dev58

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 (126) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DMcymmpc.js → ConnectedDataExplorerComponent-CJaUI-S-.js} +3 -3
  2. package/dist/{any-language-editor-BfTj3PPF.js → any-language-editor-P9tjkj4n.js} +18 -18
  3. package/dist/{architectureDiagram-VXUJARFQ-B3jN8fkV.js → architectureDiagram-VXUJARFQ-DXwkyAaM.js} +5 -5
  4. package/dist/{blockDiagram-VD42YOAC-Dq_8lAbz.js → blockDiagram-VD42YOAC-CIOjvxeD.js} +5 -5
  5. package/dist/{c4Diagram-YG6GDRKO-BPzX9a9A.js → c4Diagram-YG6GDRKO-B6dZC_oU.js} +4 -4
  6. package/dist/{channel-D_qwSlm9.js → channel-BN070S0F.js} +1 -1
  7. package/dist/{chat-ui-CYGIiYqI.js → chat-ui-BdSDiVjk.js} +79 -74
  8. package/dist/{chunk-ABZYJK2D-CDSMdLBr.js → chunk-ABZYJK2D-BxBDji1u.js} +1 -1
  9. package/dist/{chunk-ATLVNIR6-BAW0e_d_.js → chunk-ATLVNIR6-qFiszptQ.js} +1 -1
  10. package/dist/{chunk-B4BG7PRW-CfEI05WL.js → chunk-B4BG7PRW-B_z2FEHm.js} +4 -4
  11. package/dist/{chunk-DI55MBZ5-CZS7hhjV.js → chunk-DI55MBZ5-D4Y8yV8B.js} +4 -4
  12. package/dist/{chunk-EXTU4WIE-DHwKB8ls.js → chunk-EXTU4WIE-BQPBhApE.js} +1 -1
  13. package/dist/{chunk-JA3XYJ7Z-Cy80gDpz.js → chunk-JA3XYJ7Z-CY4CRbV5.js} +2 -2
  14. package/dist/{chunk-JZLCHNYA-yfXq27Zq.js → chunk-JZLCHNYA-DWIR9m5O.js} +4 -4
  15. package/dist/{chunk-N4CR4FBY-C9D3Wsot.js → chunk-N4CR4FBY-Bw2HcMNc.js} +5 -5
  16. package/dist/{chunk-QN33PNHL-Bh2YDyg7.js → chunk-QN33PNHL-DPBNNzpR.js} +1 -1
  17. package/dist/{chunk-QXUST7PY-kDpsCDoY.js → chunk-QXUST7PY-DtC2t9Mk.js} +5 -5
  18. package/dist/{chunk-S3R3BYOJ-DkY8Kz3y.js → chunk-S3R3BYOJ-BdgLc8AM.js} +1 -1
  19. package/dist/{chunk-TZMSLE5B-CMZoW3vd.js → chunk-TZMSLE5B-BzSHcq-K.js} +1 -1
  20. package/dist/{classDiagram-2ON5EDUG-b2d3ZqfR.js → classDiagram-2ON5EDUG-BiQPjMNY.js} +10 -10
  21. package/dist/{classDiagram-v2-WZHVMYZB-RvnJbJ5M.js → classDiagram-v2-WZHVMYZB-YHdR8fqM.js} +10 -10
  22. package/dist/{code-visibility-6VwDeiPl.js → code-visibility-BwRCU9D5.js} +9 -9
  23. package/dist/{dagre-6UL2VRFP--ruFWg_S.js → dagre-6UL2VRFP-DpYjBqQs.js} +7 -7
  24. package/dist/{diagram-PSM6KHXK-CGakapLj.js → diagram-PSM6KHXK-D4ftnaua.js} +6 -6
  25. package/dist/{diagram-QEK2KX5R-CtqD8u_P.js → diagram-QEK2KX5R-DVlUOgs1.js} +4 -4
  26. package/dist/{diagram-S2PKOQOG-BS1wa35J.js → diagram-S2PKOQOG-CGh4OMEV.js} +4 -4
  27. package/dist/{dist-BGLXXYL4.js → dist-3NCqaWjt.js} +2 -2
  28. package/dist/{dist-95kpvRSY.js → dist-4lVIWUT3.js} +1 -1
  29. package/dist/dist-5o9uz19x.js +5 -0
  30. package/dist/{dist-B1k1NmGB.js → dist-B2jmQKKx.js} +2 -2
  31. package/dist/{dist-kcg3gjx6.js → dist-BCX980mU.js} +2 -2
  32. package/dist/{dist-c6wTyIhD.js → dist-BIEt-mlF.js} +1 -1
  33. package/dist/{dist-tJic1rC-.js → dist-BO5HRiTf.js} +2 -2
  34. package/dist/{dist-3MT9i_N5.js → dist-BOMbzyb3.js} +85 -85
  35. package/dist/{dist-Dx3eDsHX.js → dist-BP39XM8u.js} +1 -1
  36. package/dist/{dist-DijFs2Lk.js → dist-BPF0gTRZ.js} +6 -6
  37. package/dist/{dist-BvCrjp81.js → dist-BTdrwBWP.js} +22 -22
  38. package/dist/{dist-CLBjD8zw.js → dist-BdqCKQ1O.js} +2 -2
  39. package/dist/dist-BpDH0DEP.js +5 -0
  40. package/dist/dist-BpXhbQXO.js +8 -0
  41. package/dist/{dist-DgZczohI.js → dist-BpeiYpG2.js} +2 -2
  42. package/dist/{dist-Db1W16d-.js → dist-BwhXBwMd.js} +1 -1
  43. package/dist/dist-ByadAk_L.js +5 -0
  44. package/dist/{dist-B69xyr_D.js → dist-C18MUWTj.js} +2 -2
  45. package/dist/{dist-DPdzbDlL.js → dist-C2_4pjuS.js} +1 -1
  46. package/dist/{dist-B56Dml3A.js → dist-CFSb_Frm.js} +1 -1
  47. package/dist/dist-CK0g7JvP.js +8 -0
  48. package/dist/{dist-CNXNPADS.js → dist-CRmSjkLP.js} +2 -2
  49. package/dist/{dist-D3T8fCFo.js → dist-CSlJrrAw.js} +86 -81
  50. package/dist/dist-CfzCtiq2.js +5 -0
  51. package/dist/{dist-4-bJUNMb.js → dist-D3bdsa1d.js} +4 -4
  52. package/dist/dist-D8JtVFrW.js +8 -0
  53. package/dist/dist-DK2Oocpu.js +6 -0
  54. package/dist/{dist-DBN2w27P.js → dist-DZwp8lro.js} +3 -3
  55. package/dist/{dist-BFgLBSJb.js → dist-DaXJPX8F.js} +2 -2
  56. package/dist/{dist-U4F-tbMs.js → dist-De-GlSAs.js} +246 -184
  57. package/dist/{dist-DPH6RMhL.js → dist-DgjbnqTg.js} +125 -125
  58. package/dist/dist-DnSu977r.js +5 -0
  59. package/dist/{dist-e29IFSI0.js → dist-DviUBLUp.js} +4 -4
  60. package/dist/{dist-BIdzGb3O.js → dist-DwqBJ014.js} +2 -2
  61. package/dist/{dist-BGj6VvC-.js → dist-JGehap7K.js} +5 -5
  62. package/dist/{dist-BGtkYshf.js → dist-LpLXa9Lb.js} +2 -2
  63. package/dist/dist-UmxJm6sW.js +8 -0
  64. package/dist/dist-aYSBQSyM.js +5 -0
  65. package/dist/dist-kkZ1Ztlh.js +6 -0
  66. package/dist/dist-ngPczwNi.js +5 -0
  67. package/dist/{dist-C-RLuUaE.js → dist-pqIwD8VD.js} +2 -2
  68. package/dist/dist-wQRyxyTV.js +5 -0
  69. package/dist/{dist-DvOfvWFi.js → dist-w_gWE5Wf.js} +1 -1
  70. package/dist/{erDiagram-Q2GNP2WA-DmtIJBsT.js → erDiagram-Q2GNP2WA-BcKAHFqF.js} +10 -10
  71. package/dist/{esm-D6_uCZ4z.js → esm-DKOv6P2M.js} +341 -341
  72. package/dist/esm-k8PIvuZW.js +1180 -0
  73. package/dist/{extends-B88Yrunj.js → extends-Cq2JYHKy.js} +32 -16
  74. package/dist/{flowDiagram-NV44I4VS-DZsgBUcQ.js → flowDiagram-NV44I4VS-BRhSg-ro.js} +10 -10
  75. package/dist/{formats-DSfNdJLH.js → formats-BFhkZJwd.js} +1 -1
  76. package/dist/{ganttDiagram-JELNMOA3-CgFhg_6K.js → ganttDiagram-JELNMOA3-CXXCN7LN.js} +3 -3
  77. package/dist/{gitGraphDiagram-V2S2FVAM-DPInyn1F.js → gitGraphDiagram-V2S2FVAM-BOlkMSqK.js} +3 -3
  78. package/dist/{html-to-image-BoXPmWps.js → html-to-image-DDENewl2.js} +34 -34
  79. package/dist/{infoDiagram-HS3SLOUP-ETAzKIiE.js → infoDiagram-HS3SLOUP-D3TyKnL_.js} +3 -3
  80. package/dist/{journeyDiagram-XKPGCS4Q-Bc94zDj1.js → journeyDiagram-XKPGCS4Q-DEal0aT1.js} +3 -3
  81. package/dist/{kanban-definition-3W4ZIXB7-DSk8Ebci.js → kanban-definition-3W4ZIXB7-Dl90wENN.js} +7 -7
  82. package/dist/{loader-BUGT0KbC.js → loader-CTHCI-qe.js} +7 -7
  83. package/dist/main.js +31 -31
  84. package/dist/{mermaid-CYkbfJHW.js → mermaid-CdSvixW2.js} +36 -36
  85. package/dist/{mindmap-definition-VGOIOE7T-BYoCqVAJ.js → mindmap-definition-VGOIOE7T-CCDupjWW.js} +9 -9
  86. package/dist/{pieDiagram-ADFJNKIX-BaAU79Vb.js → pieDiagram-ADFJNKIX-JvXG_6hk.js} +4 -4
  87. package/dist/{process-output-CbRRhwQo.js → process-output-2QPcgBQg.js} +1 -1
  88. package/dist/purify.es-EnR5yhgI.js +783 -0
  89. package/dist/{quadrantDiagram-AYHSOK5B-BmTWJxzo.js → quadrantDiagram-AYHSOK5B-BqDB9tUC.js} +2 -2
  90. package/dist/{react-vega-DfJOSnj6.js → react-vega-BazDALhI.js} +633 -633
  91. package/dist/{requirementDiagram-UZGBJVZJ-Jrs04ua1.js → requirementDiagram-UZGBJVZJ-KbVt8SPH.js} +9 -9
  92. package/dist/{reveal-component-BF9bUh1w.js → reveal-component-y1Kk4Glg.js} +11 -11
  93. package/dist/{sankeyDiagram-TZEHDZUN-YJnhmG4N.js → sankeyDiagram-TZEHDZUN-DgkF38jx.js} +2 -2
  94. package/dist/{sequenceDiagram-WL72ISMW-D30L51Bn.js → sequenceDiagram-WL72ISMW-BDmx9n95.js} +4 -4
  95. package/dist/{stateDiagram-FKZM4ZOC-C0nZuUP8.js → stateDiagram-FKZM4ZOC-ByOvQf2n.js} +10 -10
  96. package/dist/{stateDiagram-v2-4FDKWEC3-BmovmS-G.js → stateDiagram-v2-4FDKWEC3-C3xqeavR.js} +10 -10
  97. package/dist/style.css +1 -1
  98. package/dist/{timeline-definition-IT6M3QCI-DEoIEWAl.js → timeline-definition-IT6M3QCI-CkD_trIn.js} +2 -2
  99. package/dist/{vega-component-uL9ZaXcH.js → vega-component-CrFoZT1S.js} +4 -4
  100. package/dist/{vega-embed-container-DgZMMVL3.js → vega-embed-container-DAQAMjiO.js} +2 -2
  101. package/dist/vega-loader.browser-DmpxCJqx.js +1353 -0
  102. package/dist/{xychartDiagram-PRI3JC2R-iwKXEnYZ.js → xychartDiagram-PRI3JC2R-BSkNxYNw.js} +5 -5
  103. package/package.json +1 -1
  104. package/src/components/editor/chrome/panels/write-secret-modal.tsx +3 -2
  105. package/src/components/editor/connections/__tests__/secret-combobox.test.ts +134 -0
  106. package/src/components/editor/connections/database/schemas.ts +35 -27
  107. package/src/components/editor/connections/form-renderers.tsx +42 -110
  108. package/src/components/editor/connections/secret-combobox.tsx +253 -0
  109. package/src/components/editor/connections/storage/schemas.ts +12 -6
  110. package/dist/dist-9yiSURLw.js +0 -6
  111. package/dist/dist-9zfbSqew.js +0 -8
  112. package/dist/dist-BZ_VEpg8.js +0 -8
  113. package/dist/dist-BdYVjH1C.js +0 -5
  114. package/dist/dist-BiIoKp92.js +0 -6
  115. package/dist/dist-Bow9AUXt.js +0 -5
  116. package/dist/dist-C8odMDp5.js +0 -8
  117. package/dist/dist-CLgjZx38.js +0 -5
  118. package/dist/dist-CnMOJtGP.js +0 -5
  119. package/dist/dist-D-5m1nDT.js +0 -5
  120. package/dist/dist-D8AXqiCV.js +0 -5
  121. package/dist/dist-DMuf8sZd.js +0 -5
  122. package/dist/dist-DszXOVSb.js +0 -5
  123. package/dist/dist-GW0m0q65.js +0 -8
  124. package/dist/esm-BxhNWHqh.js +0 -1171
  125. package/dist/purify.es-C-fIGuQm.js +0 -557
  126. package/dist/vega-loader.browser-IU8eTDwM.js +0 -1333
@@ -1,17 +1,17 @@
1
1
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2
- import "./purify.es-C-fIGuQm.js";
2
+ import "./purify.es-EnR5yhgI.js";
3
3
  import { t as linear } from "./linear-H_vsH9ao.js";
4
4
  import { n as ordinal } from "./ordinal-Bkv2-veX.js";
5
5
  import { t as range } from "./range-BOiA8qqU.js";
6
6
  import "./defaultLocale-BpsHxBd7.js";
7
7
  import { t as line_default } from "./line-C43FIPBd.js";
8
- import { i as cleanAndMerge } from "./chunk-S3R3BYOJ-DkY8Kz3y.js";
8
+ import { i as cleanAndMerge } from "./chunk-S3R3BYOJ-BdgLc8AM.js";
9
9
  import { n as initRange } from "./init-DNBbYZd_.js";
10
10
  import { i as log, r as __name } from "./src-DV3ha_nA.js";
11
- import { B as setAccTitle, C as getDiagramTitle, I as sanitizeText, T as getThemeVariables3, U as setDiagramTitle, _ as getAccDescription, a as clear, c as configureSvgSize, d as defaultConfig_default, v as getAccTitle, y as getConfig, z as setAccDescription } from "./chunk-ABZYJK2D-CDSMdLBr.js";
12
- import { t as selectSvgElement } from "./chunk-EXTU4WIE-DHwKB8ls.js";
11
+ import { B as setAccTitle, C as getDiagramTitle, I as sanitizeText, T as getThemeVariables3, U as setDiagramTitle, _ as getAccDescription, a as clear, c as configureSvgSize, d as defaultConfig_default, v as getAccTitle, y as getConfig, z as setAccDescription } from "./chunk-ABZYJK2D-BxBDji1u.js";
12
+ import { t as selectSvgElement } from "./chunk-EXTU4WIE-BQPBhApE.js";
13
13
  import "./dist-DsoKosxi.js";
14
- import { t as computeDimensionOfText } from "./chunk-JA3XYJ7Z-Cy80gDpz.js";
14
+ import { t as computeDimensionOfText } from "./chunk-JA3XYJ7Z-CY4CRbV5.js";
15
15
  function band() {
16
16
  var e = ordinal().unknown(void 0), w = e.domain, T = e.range, D = 0, O = 1, k, A, j = false, M = 0, N = 0, P = 0.5;
17
17
  delete e.unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.15-dev55",
3
+ "version": "0.23.15-dev58",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -43,10 +43,11 @@ export const WriteSecretModal: React.FC<{
43
43
  providerNames: string[];
44
44
  onClose: () => void;
45
45
  onSuccess: (secretName: string) => void;
46
- }> = ({ providerNames, onClose, onSuccess }) => {
46
+ initialValue?: string;
47
+ }> = ({ providerNames, onClose, onSuccess, initialValue }) => {
47
48
  const { writeSecret } = useRequestClient();
48
49
  const [key, setKey] = React.useState("");
49
- const [value, setValue] = React.useState("");
50
+ const [value, setValue] = React.useState(initialValue || "");
50
51
  const [location, setLocation] = React.useState<string | undefined>(
51
52
  providerNames[0],
52
53
  );
@@ -0,0 +1,134 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { describe, expect, test } from "vitest";
3
+ import { partitionSecretKeys } from "../secret-combobox";
4
+
5
+ describe("partitionSecretKeys", () => {
6
+ const keys = [
7
+ "HOSTNAME",
8
+ "PGHOST",
9
+ "DB_HOST",
10
+ "KUBERNETES_SERVICE_HOST",
11
+ "GPG_KEY",
12
+ "HOME",
13
+ "POSTGRES_PASSWORD",
14
+ "DB_PASSWORD",
15
+ "USERNAME",
16
+ "PGUSER",
17
+ "USER_AGENT",
18
+ "AWS_ACCESS_KEY_ID",
19
+ "AWS_SECRET_ACCESS_KEY",
20
+ "AWS_SESSION_TOKEN",
21
+ "OBJECT_STORAGE_KEY",
22
+ "OBJECT_STORAGE_SECRET",
23
+ "DATABASE_URL",
24
+ "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
25
+ "PATH",
26
+ "GITHUB_PAT",
27
+ ];
28
+
29
+ test("returns all keys as other when regex is empty", () => {
30
+ expect(partitionSecretKeys(keys, "")).toEqual({
31
+ recommended: [],
32
+ other: keys,
33
+ });
34
+ });
35
+
36
+ test("handles invalid regex", () => {
37
+ expect(partitionSecretKeys(keys, "[invalid")).toEqual({
38
+ recommended: [],
39
+ other: keys,
40
+ });
41
+ });
42
+
43
+ test("recommends host-like keys and excludes kubernetes", () => {
44
+ const hostRegex =
45
+ "^(?!.*(kubernetes|gpg)).*(host(name)?|pghost|db.?host|database.?host|mysql.?host|postgres.?host|server.?host)";
46
+ const { recommended, other } = partitionSecretKeys(keys, hostRegex);
47
+
48
+ expect(recommended).toEqual(["HOSTNAME", "PGHOST", "DB_HOST"]);
49
+ expect(other).toContain("KUBERNETES_SERVICE_HOST");
50
+ expect(other).toContain("GPG_KEY");
51
+ expect(other).toContain("HOME");
52
+ });
53
+
54
+ test("recommends password-like keys", () => {
55
+ const { recommended } = partitionSecretKeys(
56
+ keys,
57
+ "(password|passwd|pgpassword|db.?pass(word)?)",
58
+ );
59
+ expect(recommended).toEqual(["POSTGRES_PASSWORD", "DB_PASSWORD"]);
60
+ });
61
+
62
+ test("recommends username keys without matching USER_AGENT", () => {
63
+ const { recommended } = partitionSecretKeys(
64
+ keys,
65
+ "(username|pguser|db.?user|^USER$|_USER$)",
66
+ );
67
+ expect(recommended).toEqual(["USERNAME", "PGUSER"]);
68
+ expect(recommended).not.toContain("USER_AGENT");
69
+ });
70
+
71
+ test("recommends aws access key id without matching the secret key", () => {
72
+ const { recommended } = partitionSecretKeys(
73
+ keys,
74
+ "(access.?key.?id|aws.?access.?key)",
75
+ );
76
+ expect(recommended).toEqual(["AWS_ACCESS_KEY_ID"]);
77
+ });
78
+
79
+ test("recommends aws secret access key", () => {
80
+ const { recommended } = partitionSecretKeys(
81
+ keys,
82
+ "(secret.?access.?key|aws.?secret)",
83
+ );
84
+ expect(recommended).toEqual(["AWS_SECRET_ACCESS_KEY"]);
85
+ });
86
+
87
+ test("recommends aws session token", () => {
88
+ const { recommended } = partitionSecretKeys(
89
+ keys,
90
+ "(session.?token|aws.?session)",
91
+ );
92
+ expect(recommended).toEqual(["AWS_SESSION_TOKEN"]);
93
+ });
94
+
95
+ test("recommends object storage credentials", () => {
96
+ expect(
97
+ partitionSecretKeys(
98
+ keys,
99
+ "(access.?key.?id|object.?storage.?key|aws.?access.?key)",
100
+ ).recommended,
101
+ ).toEqual(["AWS_ACCESS_KEY_ID", "OBJECT_STORAGE_KEY"]);
102
+ expect(
103
+ partitionSecretKeys(
104
+ keys,
105
+ "(secret.?access.?key|object.?storage.?secret|aws.?secret)",
106
+ ).recommended,
107
+ ).toEqual(["AWS_SECRET_ACCESS_KEY", "OBJECT_STORAGE_SECRET"]);
108
+ });
109
+
110
+ test("recommends uri/url-like keys", () => {
111
+ const { recommended } = partitionSecretKeys(
112
+ keys,
113
+ "(uri|url|connection.?string|database.?url|jdbc)",
114
+ );
115
+ expect(recommended).toEqual(["DATABASE_URL"]);
116
+ });
117
+
118
+ test("recommends token keys and bounds PAT so PATH is excluded", () => {
119
+ const { recommended, other } = partitionSecretKeys(
120
+ keys,
121
+ "(token|api.?key|access.?token|auth.?token|(^|_)pat(_|$))",
122
+ );
123
+ expect(recommended).toEqual(["AWS_SESSION_TOKEN", "GITHUB_PAT"]);
124
+ expect(other).toContain("PATH");
125
+ });
126
+
127
+ test("recommends passphrase keys", () => {
128
+ const { recommended } = partitionSecretKeys(
129
+ keys,
130
+ "(passphrase|private.?key.?passphrase|key.?passphrase)",
131
+ );
132
+ expect(recommended).toEqual(["SNOWFLAKE_PRIVATE_KEY_PASSPHRASE"]);
133
+ });
134
+ });
@@ -10,8 +10,7 @@ function passwordField() {
10
10
  FieldOptions.of({
11
11
  label: "Password",
12
12
  inputType: "password",
13
- placeholder: "password",
14
- optionRegex: ".*password.*",
13
+ optionRegex: "(password|passwd|pgpassword|db.?pass(word)?)",
15
14
  }),
16
15
  );
17
16
  }
@@ -24,8 +23,7 @@ function tokenField(label?: string, required?: boolean) {
24
23
  FieldOptions.of({
25
24
  label: label || "Token",
26
25
  inputType: "password",
27
- placeholder: "token",
28
- optionRegex: ".*token.*",
26
+ optionRegex: "(token|api.?key|access.?token|auth.?token|(^|_)pat(_|$))",
29
27
  }),
30
28
  );
31
29
  return field;
@@ -38,8 +36,7 @@ function warehouseNameField() {
38
36
  .describe(
39
37
  FieldOptions.of({
40
38
  label: "Warehouse Name",
41
- placeholder: "warehouse",
42
- optionRegex: ".*warehouse.*",
39
+ optionRegex: "(warehouse|snowflake.?warehouse)",
43
40
  }),
44
41
  );
45
42
  }
@@ -49,7 +46,10 @@ function uriField(label?: string, required?: boolean) {
49
46
  field = required ? field.nonempty() : field.optional();
50
47
 
51
48
  return field.describe(
52
- FieldOptions.of({ label: label || "URI", optionRegex: ".*uri.*" }),
49
+ FieldOptions.of({
50
+ label: label || "URI",
51
+ optionRegex: "(uri|url|connection.?string|database.?url|jdbc)",
52
+ }),
53
53
  );
54
54
  }
55
55
 
@@ -60,8 +60,9 @@ function hostField(label?: string) {
60
60
  .describe(
61
61
  FieldOptions.of({
62
62
  label: label || "Host",
63
- placeholder: "localhost",
64
- optionRegex: ".*host.*",
63
+ // Exclude kubernetes/system host vars that match a naive "host" search.
64
+ optionRegex:
65
+ "^(?!.*(kubernetes|gpg)).*(host(name)?|pghost|db.?host|database.?host|mysql.?host|postgres.?host|server.?host)",
65
66
  }),
66
67
  );
67
68
  }
@@ -70,8 +71,7 @@ function databaseField() {
70
71
  return z.string().describe(
71
72
  FieldOptions.of({
72
73
  label: "Database",
73
- placeholder: "db name",
74
- optionRegex: ".*database.*",
74
+ optionRegex: "(database|db.?name|pgdatabase|^DB$|^DATABASE$)",
75
75
  }),
76
76
  );
77
77
  }
@@ -80,8 +80,7 @@ function schemaField() {
80
80
  return z.string().describe(
81
81
  FieldOptions.of({
82
82
  label: "Schema",
83
- placeholder: "schema name",
84
- optionRegex: ".*schema.*",
83
+ optionRegex: "(^SCHEMA$|db.?schema|postgres.?schema|pg.?schema|_SCHEMA$)",
85
84
  }),
86
85
  );
87
86
  }
@@ -93,8 +92,7 @@ function usernameField() {
93
92
  .describe(
94
93
  FieldOptions.of({
95
94
  label: "Username",
96
- placeholder: "username",
97
- optionRegex: ".*username.*",
95
+ optionRegex: "(username|pguser|db.?user|^USER$|_USER$)",
98
96
  }),
99
97
  );
100
98
  }
@@ -196,7 +194,7 @@ export const SnowflakeConnectionSchema = z
196
194
  .describe(
197
195
  FieldOptions.of({
198
196
  label: "Account",
199
- optionRegex: ".*snowflake.*",
197
+ optionRegex: "(snowflake.?account|^SNOWFLAKE_ACCOUNT$|account.?id)",
200
198
  }),
201
199
  ),
202
200
  warehouse: z
@@ -205,7 +203,7 @@ export const SnowflakeConnectionSchema = z
205
203
  .describe(
206
204
  FieldOptions.of({
207
205
  label: "Warehouse",
208
- optionRegex: ".*snowflake.*",
206
+ optionRegex: "(snowflake.?warehouse|warehouse)",
209
207
  }),
210
208
  ),
211
209
  database: databaseField(),
@@ -215,7 +213,7 @@ export const SnowflakeConnectionSchema = z
215
213
  .describe(
216
214
  FieldOptions.of({
217
215
  label: "Schema",
218
- optionRegex: ".*snowflake.*",
216
+ optionRegex: "(snowflake.?schema|^SCHEMA$|_SCHEMA$)",
219
217
  }),
220
218
  ),
221
219
  role: z
@@ -256,7 +254,8 @@ export const SnowflakeConnectionSchema = z
256
254
  FieldOptions.of({
257
255
  label: "Private Key Passphrase",
258
256
  inputType: "password",
259
- optionRegex: ".*passphrase.*",
257
+ optionRegex:
258
+ "(passphrase|private.?key.?passphrase|key.?passphrase)",
260
259
  }),
261
260
  ),
262
261
  }),
@@ -283,7 +282,8 @@ export const BigQueryConnectionSchema = z
283
282
  .describe(
284
283
  FieldOptions.of({
285
284
  label: "Project ID",
286
- optionRegex: ".*bigquery.*",
285
+ optionRegex:
286
+ "(bigquery.?project|gcp.?project|google.?cloud.?project|project.?id)",
287
287
  }),
288
288
  ),
289
289
  dataset: z
@@ -292,7 +292,7 @@ export const BigQueryConnectionSchema = z
292
292
  .describe(
293
293
  FieldOptions.of({
294
294
  label: "Dataset",
295
- optionRegex: ".*bigquery.*",
295
+ optionRegex: "(bigquery.?dataset|dataset)",
296
296
  }),
297
297
  ),
298
298
  credentials_json: z
@@ -377,7 +377,7 @@ export const IcebergConnectionSchema = z.object({
377
377
  FieldOptions.of({
378
378
  label: "URI",
379
379
  placeholder: "https://",
380
- optionRegex: ".*uri.*",
380
+ optionRegex: "(uri|url|connection.?string|database.?url|jdbc)",
381
381
  }),
382
382
  ),
383
383
  token: tokenField(),
@@ -392,7 +392,7 @@ export const IcebergConnectionSchema = z.object({
392
392
  FieldOptions.of({
393
393
  label: "URI",
394
394
  placeholder: "jdbc:iceberg://host:port/database",
395
- optionRegex: ".*uri.*",
395
+ optionRegex: "(uri|url|connection.?string|database.?url|jdbc)",
396
396
  }),
397
397
  ),
398
398
  }),
@@ -419,7 +419,13 @@ export const IcebergConnectionSchema = z.object({
419
419
  "dynamodb.access-key-id": z
420
420
  .string()
421
421
  .optional()
422
- .describe(FieldOptions.of({ label: "Access Key ID" })),
422
+ .describe(
423
+ FieldOptions.of({
424
+ label: "Access Key ID",
425
+ inputType: "password",
426
+ optionRegex: "(access.?key.?id|aws.?access.?key)",
427
+ }),
428
+ ),
423
429
  "dynamodb.secret-access-key": z
424
430
  .string()
425
431
  .optional()
@@ -427,6 +433,7 @@ export const IcebergConnectionSchema = z.object({
427
433
  FieldOptions.of({
428
434
  label: "Secret Access Key",
429
435
  inputType: "password",
436
+ optionRegex: "(secret.?access.?key|aws.?secret)",
430
437
  }),
431
438
  ),
432
439
  "dynamodb.session-token": z
@@ -436,6 +443,7 @@ export const IcebergConnectionSchema = z.object({
436
443
  FieldOptions.of({
437
444
  label: "Session Token",
438
445
  inputType: "password",
446
+ optionRegex: "(session.?token|aws.?session)",
439
447
  }),
440
448
  ),
441
449
  }),
@@ -484,7 +492,7 @@ export const RedshiftConnectionSchema = z
484
492
  FieldOptions.of({
485
493
  label: "AWS Access Key ID",
486
494
  inputType: "password",
487
- optionRegex: ".*aws_access_key_id.*",
495
+ optionRegex: "(access.?key.?id|aws.?access.?key)",
488
496
  }),
489
497
  ),
490
498
  aws_secret_access_key: z
@@ -494,7 +502,7 @@ export const RedshiftConnectionSchema = z
494
502
  FieldOptions.of({
495
503
  label: "AWS Secret Access Key",
496
504
  inputType: "password",
497
- optionRegex: ".*aws_secret_access_key.*",
505
+ optionRegex: "(secret.?access.?key|aws.?secret)",
498
506
  }),
499
507
  ),
500
508
  aws_session_token: z
@@ -504,7 +512,7 @@ export const RedshiftConnectionSchema = z
504
512
  FieldOptions.of({
505
513
  label: "AWS Session Token",
506
514
  inputType: "password",
507
- optionRegex: ".*aws_session_token.*",
515
+ optionRegex: "(session.?token|aws.?session)",
508
516
  }),
509
517
  ),
510
518
  }),
@@ -1,20 +1,10 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { KeyIcon, PlusCircleIcon } from "lucide-react";
4
- import { createContext, type ReactNode, use } from "react";
3
+ import { createContext, type ReactNode, use, useMemo } from "react";
5
4
  import { z } from "zod";
6
5
  import type { FormRenderer } from "@/components/forms/form";
7
6
  import { FieldOptions } from "@/components/forms/options";
8
7
  import { useImperativeModal } from "@/components/modal/ImperativeModal";
9
- import { Button } from "@/components/ui/button";
10
- import {
11
- DropdownMenu,
12
- DropdownMenuContent,
13
- DropdownMenuItem,
14
- DropdownMenuLabel,
15
- DropdownMenuSeparator,
16
- DropdownMenuTrigger,
17
- } from "@/components/ui/dropdown-menu";
18
8
  import {
19
9
  FormControl,
20
10
  FormDescription,
@@ -23,18 +13,15 @@ import {
23
13
  FormLabel,
24
14
  FormMessage,
25
15
  } from "@/components/ui/form";
26
- import { Input } from "@/components/ui/input";
27
- import { NumberField } from "@/components/ui/number-field";
28
16
  import { SECRETS_REGISTRY } from "@/core/secrets/request-registry";
29
17
  import { useAsyncData } from "@/hooks/useAsyncData";
30
- import { partition } from "@/utils/arrays";
31
- import { cn } from "@/utils/cn";
32
18
  import { Functions } from "@/utils/functions";
33
19
  import {
34
20
  sortProviders,
35
21
  WriteSecretModal,
36
22
  } from "../chrome/panels/write-secret-modal";
37
- import { displaySecret, isSecret, prefixSecret } from "./secrets";
23
+ import { partitionSecretKeys, SecretCombobox } from "./secret-combobox";
24
+ import { prefixSecret } from "./secrets";
38
25
 
39
26
  interface SecretsContextType {
40
27
  providerNames: string[];
@@ -77,25 +64,23 @@ export const SecretsProvider = ({ children }: SecretsProviderProps) => {
77
64
  };
78
65
  }, []);
79
66
 
80
- return (
81
- <SecretsContext
82
- value={{
83
- secretKeys: data?.secretKeys || [],
84
- providerNames: data?.providerNames || [],
85
- loading: isPending,
86
- error,
87
- refreshSecrets: reload,
88
- }}
89
- >
90
- {children}
91
- </SecretsContext>
67
+ const value = useMemo(
68
+ () => ({
69
+ secretKeys: data?.secretKeys || [],
70
+ providerNames: data?.providerNames || [],
71
+ loading: isPending,
72
+ error,
73
+ refreshSecrets: reload,
74
+ }),
75
+ [data?.secretKeys, data?.providerNames, isPending, error, reload],
92
76
  );
77
+
78
+ return <SecretsContext value={value}>{children}</SecretsContext>;
93
79
  };
94
80
 
95
- export const ENV_RENDERER: FormRenderer<z.ZodString | z.ZodNumber> = {
96
- isMatch: (schema: z.ZodType): schema is z.ZodString | z.ZodNumber => {
97
- // string or number with optionsRegex
98
- if (schema instanceof z.ZodString || schema instanceof z.ZodNumber) {
81
+ export const ENV_RENDERER: FormRenderer<z.ZodString> = {
82
+ isMatch: (schema: z.ZodType): schema is z.ZodString => {
83
+ if (schema instanceof z.ZodString) {
99
84
  const { optionRegex } = FieldOptions.parse(schema.description || "");
100
85
  return Boolean(optionRegex);
101
86
  }
@@ -109,12 +94,13 @@ export const ENV_RENDERER: FormRenderer<z.ZodString | z.ZodNumber> = {
109
94
  const {
110
95
  label,
111
96
  description,
97
+ placeholder,
112
98
  optionRegex = "",
99
+ inputType,
113
100
  } = FieldOptions.parse(schema.description || "");
114
101
 
115
- const [recommendedKeys, otherKeys] = partition(secretKeys, (key) =>
116
- new RegExp(optionRegex, "i").test(key),
117
- );
102
+ const secretsOnly = inputType === "password";
103
+ const { recommended, other } = partitionSecretKeys(secretKeys, optionRegex);
118
104
 
119
105
  return (
120
106
  <FormField
@@ -125,82 +111,28 @@ export const ENV_RENDERER: FormRenderer<z.ZodString | z.ZodNumber> = {
125
111
  <FormLabel>{label}</FormLabel>
126
112
  <FormDescription>{description}</FormDescription>
127
113
  <FormControl>
128
- <div className="flex gap-2">
129
- {schema instanceof z.ZodString ? (
130
- <Input
131
- {...field}
132
- value={displaySecret(field.value as string)}
133
- onChange={field.onChange}
134
- className={cn("flex-1")}
135
- />
136
- ) : (
137
- <NumberField
138
- {...field}
139
- value={field.value as number}
140
- onChange={field.onChange}
141
- className="flex-1"
142
- />
143
- )}
144
- <DropdownMenu>
145
- <DropdownMenuTrigger asChild={true}>
146
- <Button
147
- variant="outline"
148
- size="icon"
149
- className={cn(
150
- isSecret(field.value as string) && "bg-accent",
151
- )}
152
- >
153
- <KeyIcon className="h-3 w-3" />
154
- </Button>
155
- </DropdownMenuTrigger>
156
- <DropdownMenuContent
157
- align="end"
158
- className="max-h-60 overflow-y-auto"
159
- >
160
- <DropdownMenuItem
161
- onSelect={() => {
162
- openModal(
163
- <WriteSecretModal
164
- providerNames={providerNames}
165
- onSuccess={(secretKey) => {
166
- refreshSecrets();
167
- field.onChange(prefixSecret(secretKey));
168
- closeModal();
169
- }}
170
- onClose={closeModal}
171
- />,
172
- );
114
+ <SecretCombobox
115
+ value={field.value ? String(field.value) : ""}
116
+ onChange={field.onChange}
117
+ placeholder={placeholder}
118
+ secretsOnly={secretsOnly}
119
+ recommendedKeys={recommended}
120
+ otherKeys={other}
121
+ onCreateSecret={(suggestedValue) => {
122
+ openModal(
123
+ <WriteSecretModal
124
+ providerNames={providerNames}
125
+ initialValue={suggestedValue}
126
+ onSuccess={(secretKey) => {
127
+ refreshSecrets();
128
+ field.onChange(prefixSecret(secretKey));
129
+ closeModal();
173
130
  }}
174
- >
175
- <PlusCircleIcon className="mr-2 h-3.5 w-3.5" />
176
- Create a new secret
177
- </DropdownMenuItem>
178
- {recommendedKeys.length > 0 && (
179
- <>
180
- <DropdownMenuSeparator />
181
- <DropdownMenuLabel>Recommended</DropdownMenuLabel>
182
- </>
183
- )}
184
- {recommendedKeys.map((key) => (
185
- <DropdownMenuItem
186
- key={key}
187
- onSelect={() => field.onChange(prefixSecret(key))}
188
- >
189
- {displaySecret(key)}
190
- </DropdownMenuItem>
191
- ))}
192
- {otherKeys.length > 0 && <DropdownMenuSeparator />}
193
- {otherKeys.map((key) => (
194
- <DropdownMenuItem
195
- key={key}
196
- onSelect={() => field.onChange(prefixSecret(key))}
197
- >
198
- {displaySecret(key)}
199
- </DropdownMenuItem>
200
- ))}
201
- </DropdownMenuContent>
202
- </DropdownMenu>
203
- </div>
131
+ onClose={closeModal}
132
+ />,
133
+ );
134
+ }}
135
+ />
204
136
  </FormControl>
205
137
  <FormMessage />
206
138
  </FormItem>