viberoom 0.6.1 → 0.8.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 (115) hide show
  1. package/NOTICE +14 -0
  2. package/README.md +11 -0
  3. package/dist/hub.js +101 -14
  4. package/dist/look-lint.js +180 -0
  5. package/dist/looks.js +151 -0
  6. package/dist/mcp-skills-server.js +87 -0
  7. package/dist/persona.js +2 -0
  8. package/dist/room.js +134 -4
  9. package/dist/server.js +70 -0
  10. package/dist/skills.js +23 -2
  11. package/package.json +1 -1
  12. package/ui/app.css +142 -114
  13. package/ui/app.js +423 -102
  14. package/ui/avatars.js +7 -14
  15. package/ui/components.css +156 -80
  16. package/ui/components.js +207 -1
  17. package/ui/fonts/anonymous-pro-cyrillic-400.woff2 +0 -0
  18. package/ui/fonts/anonymous-pro-cyrillic-700.woff2 +0 -0
  19. package/ui/fonts/anonymous-pro-latin-400.woff2 +0 -0
  20. package/ui/fonts/anonymous-pro-latin-700.woff2 +0 -0
  21. package/ui/fonts/anonymous-pro-latin-ext-400.woff2 +0 -0
  22. package/ui/fonts/anonymous-pro-latin-ext-700.woff2 +0 -0
  23. package/ui/fonts/anonymous-pro.css +7 -0
  24. package/ui/fonts/cascadia-code-cyrillic-ext.woff2 +0 -0
  25. package/ui/fonts/cascadia-code-cyrillic.woff2 +0 -0
  26. package/ui/fonts/cascadia-code-latin-ext.woff2 +0 -0
  27. package/ui/fonts/cascadia-code-latin.woff2 +0 -0
  28. package/ui/fonts/cascadia-code.css +5 -0
  29. package/ui/fonts/comfortaa-cyrillic-ext.woff2 +0 -0
  30. package/ui/fonts/comfortaa-cyrillic.woff2 +0 -0
  31. package/ui/fonts/comfortaa-latin-ext.woff2 +0 -0
  32. package/ui/fonts/comfortaa-latin.woff2 +0 -0
  33. package/ui/fonts/comfortaa.css +5 -0
  34. package/ui/fonts/exo-2-cyrillic-ext.woff2 +0 -0
  35. package/ui/fonts/exo-2-cyrillic.woff2 +0 -0
  36. package/ui/fonts/exo-2-latin-ext.woff2 +0 -0
  37. package/ui/fonts/exo-2-latin.woff2 +0 -0
  38. package/ui/fonts/exo-2.css +5 -0
  39. package/ui/fonts/golos-text-cyrillic-ext.woff2 +0 -0
  40. package/ui/fonts/golos-text-cyrillic.woff2 +0 -0
  41. package/ui/fonts/golos-text-latin-ext.woff2 +0 -0
  42. package/ui/fonts/golos-text-latin.woff2 +0 -0
  43. package/ui/fonts/golos-text.css +5 -0
  44. package/ui/fonts/ibm-plex-mono-cyrillic-400.woff2 +0 -0
  45. package/ui/fonts/ibm-plex-mono-cyrillic-600.woff2 +0 -0
  46. package/ui/fonts/ibm-plex-mono-cyrillic-700.woff2 +0 -0
  47. package/ui/fonts/ibm-plex-mono-cyrillic-ext-400.woff2 +0 -0
  48. package/ui/fonts/ibm-plex-mono-cyrillic-ext-600.woff2 +0 -0
  49. package/ui/fonts/ibm-plex-mono-cyrillic-ext-700.woff2 +0 -0
  50. package/ui/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
  51. package/ui/fonts/ibm-plex-mono-latin-600.woff2 +0 -0
  52. package/ui/fonts/ibm-plex-mono-latin-700.woff2 +0 -0
  53. package/ui/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
  54. package/ui/fonts/ibm-plex-mono-latin-ext-600.woff2 +0 -0
  55. package/ui/fonts/ibm-plex-mono-latin-ext-700.woff2 +0 -0
  56. package/ui/fonts/ibm-plex-mono.css +13 -0
  57. package/ui/fonts/ibm-plex-sans-cyrillic-400.woff2 +0 -0
  58. package/ui/fonts/ibm-plex-sans-cyrillic-600.woff2 +0 -0
  59. package/ui/fonts/ibm-plex-sans-cyrillic-700.woff2 +0 -0
  60. package/ui/fonts/ibm-plex-sans-cyrillic-ext-400.woff2 +0 -0
  61. package/ui/fonts/ibm-plex-sans-cyrillic-ext-600.woff2 +0 -0
  62. package/ui/fonts/ibm-plex-sans-cyrillic-ext-700.woff2 +0 -0
  63. package/ui/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
  64. package/ui/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
  65. package/ui/fonts/ibm-plex-sans-latin-700.woff2 +0 -0
  66. package/ui/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
  67. package/ui/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
  68. package/ui/fonts/ibm-plex-sans-latin-ext-700.woff2 +0 -0
  69. package/ui/fonts/ibm-plex-sans.css +13 -0
  70. package/ui/fonts/manrope-cyrillic-ext.woff2 +0 -0
  71. package/ui/fonts/manrope-cyrillic.woff2 +0 -0
  72. package/ui/fonts/manrope-latin-ext.woff2 +0 -0
  73. package/ui/fonts/manrope-latin.woff2 +0 -0
  74. package/ui/fonts/manrope.css +5 -0
  75. package/ui/fonts/montserrat-cyrillic-ext.woff2 +0 -0
  76. package/ui/fonts/montserrat-cyrillic.woff2 +0 -0
  77. package/ui/fonts/montserrat-latin-ext.woff2 +0 -0
  78. package/ui/fonts/montserrat-latin.woff2 +0 -0
  79. package/ui/fonts/montserrat.css +5 -0
  80. package/ui/fonts/open-sans-cyrillic-ext.woff2 +0 -0
  81. package/ui/fonts/open-sans-cyrillic.woff2 +0 -0
  82. package/ui/fonts/open-sans-latin-ext.woff2 +0 -0
  83. package/ui/fonts/open-sans-latin.woff2 +0 -0
  84. package/ui/fonts/open-sans.css +5 -0
  85. package/ui/fonts/pt-mono-cyrillic-ext.woff2 +0 -0
  86. package/ui/fonts/pt-mono-cyrillic.woff2 +0 -0
  87. package/ui/fonts/pt-mono-latin-ext.woff2 +0 -0
  88. package/ui/fonts/pt-mono-latin.woff2 +0 -0
  89. package/ui/fonts/pt-mono.css +5 -0
  90. package/ui/fonts/rubik-cyrillic-ext.woff2 +0 -0
  91. package/ui/fonts/rubik-cyrillic.woff2 +0 -0
  92. package/ui/fonts/rubik-latin-ext.woff2 +0 -0
  93. package/ui/fonts/rubik-latin.woff2 +0 -0
  94. package/ui/fonts/rubik.css +5 -0
  95. package/ui/fonts/source-sans-3-cyrillic-ext.woff2 +0 -0
  96. package/ui/fonts/source-sans-3-cyrillic.woff2 +0 -0
  97. package/ui/fonts/source-sans-3-latin-ext.woff2 +0 -0
  98. package/ui/fonts/source-sans-3-latin.woff2 +0 -0
  99. package/ui/fonts/source-sans-3.css +5 -0
  100. package/ui/fonts/ubuntu-sans-cyrillic-ext.woff2 +0 -0
  101. package/ui/fonts/ubuntu-sans-cyrillic.woff2 +0 -0
  102. package/ui/fonts/ubuntu-sans-latin-ext.woff2 +0 -0
  103. package/ui/fonts/ubuntu-sans-latin.woff2 +0 -0
  104. package/ui/fonts/ubuntu-sans.css +5 -0
  105. package/ui/fonts/victor-mono-cyrillic-ext.woff2 +0 -0
  106. package/ui/fonts/victor-mono-cyrillic.woff2 +0 -0
  107. package/ui/fonts/victor-mono-latin-ext.woff2 +0 -0
  108. package/ui/fonts/victor-mono-latin.woff2 +0 -0
  109. package/ui/fonts/victor-mono.css +5 -0
  110. package/ui/index.html +16 -6
  111. package/ui/looks.css +2831 -0
  112. package/ui/manifest.json +3 -3
  113. package/ui/theme.css +203 -96
  114. package/ui/tokens.js +800 -45
  115. package/ui/ui.js +2 -0
package/ui/tokens.js CHANGED
@@ -15,6 +15,7 @@
15
15
  const at = (u, v) => Math.round(u + (v - u) * t);
16
16
  return `#${[at(x, i), at(y, j), at(z, k)].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
17
17
  };
18
+ const NO_SHADOW = "0 0 0 0 transparent";
18
19
 
19
20
  const pop = {
20
21
  primary: "#5b5bf0",
@@ -27,7 +28,7 @@
27
28
  ink2: "#2c2b4a",
28
29
  ink3: "#5c5c80",
29
30
  indigo: "#1f1d3a",
30
- muted: "#8f8fb0",
31
+ muted: "#8080a3",
31
32
  faint: "#b3b3cc",
32
33
  placeholder: "#a3a3c0",
33
34
  grey: "#9ca3af",
@@ -50,7 +51,7 @@
50
51
  green: "#2fc97e",
51
52
  warm: "#fff3cc",
52
53
  warm2: "#ffeaa8",
53
- warmInk: "#b8860b",
54
+ warmInk: "#a07408",
54
55
  bronze: "#5a4200",
55
56
  amber: "#f5a524",
56
57
  honey: "#ffcf8f",
@@ -92,6 +93,116 @@
92
93
  magenta: "#f2b8ff",
93
94
  };
94
95
 
96
+ const terminal = {
97
+ ...pop,
98
+ primary: "#3ddc84", primaryLight: "#5ae69a", primaryDeep: "#2bc472", primaryDark: "#1fa85f",
99
+ violet: "#7ee0ff",
100
+ iris: "#0b0f0c",
101
+ ink: "#d7e3d9", ink2: "#c3d1c6", ink3: "#9fb3a5", indigo: "#e6f0e8",
102
+ muted: "#7c9282", faint: "#5b6f61", placeholder: "#5b6f61", grey: "#6f8276",
103
+ white: "#0f1411", black: "#000000",
104
+ bg: "#090c0a", soft: "#131a15", softer: "#10160f", lav: "#172019", lav2: "#1f2b22",
105
+ canvasTop: "#0a0d0b", canvasMid: "#090c0a", canvasBottom: "#080a09",
106
+ shadowInk: "#000000",
107
+ mint: "#123a26", mint2: "#164a30", mintTint: "#0f2e1f", mintInk: "#3ddc84", forest: "#8fe3bd", green: "#3ddc84",
108
+ warm: "#3a2f0c", warm2: "#4a3d10", warmInk: "#f5c542", bronze: "#ffe08a", amber: "#f5c542", honey: "#f5c542", spark: "#ffd166",
109
+ peach: "#3a2414", peach2: "#4a2d18", orange: "#ff9a3d", orangeLight: "#ffab5c", orangeDeep: "#e8782a", orangeDark: "#ffb36b", ember: "#ff8a45", emberDeep: "#f0602c",
110
+ rose: "#3a1512", rose2: "#4a1a16", roseInk: "#ff6b5e", roseDark: "#ff8578", pink: "#ff7ab8", pinkTint: "#2e1420",
111
+ sky: "#0f2a3a", blue: "#5ab0ff", blueDeep: "#7cc0ff", blueSoft: "#8cc8ff",
112
+ orchid: "#2a1a3a", orchidInk: "#c79bff", orchidMid: "#b58cf0",
113
+ periwinkle: "#233126", periwinkleDeep: "#2f4234",
114
+ deep: "#050705", deepGutter: "#0a0d0b", deepInk: "#d7e3d9", deepMuted: "#5b6f61", deepHead: "#c3d1c6", deepMeta: "#7c9282",
115
+ lavGrey: "#5f7a67", lilac: "#7ee0ff", seafoam: "#3ddc84", azure: "#5ab0ff", salmon: "#ff8578", magenta: "#ff7ab8",
116
+ };
117
+
118
+ const classicDark = {
119
+ ...pop,
120
+ primary: "#8a8aff", primaryLight: "#9c9cff", primaryDeep: "#7474f7", primaryDark: "#6262e6",
121
+ violet: "#b39cff",
122
+ iris: "#202142",
123
+ ink: "#f1efff", ink2: "#e0deff", ink3: "#c0bde6", indigo: "#f6f4ff",
124
+ muted: "#a4a2cc", faint: "#8583b0", placeholder: "#7f7dab", grey: "#8e90b0",
125
+ white: "#202142", black: "#000000",
126
+ bg: "#0f1024", soft: "#262848", softer: "#232445", lav: "#2c2e56", lav2: "#3a3c6e",
127
+ canvasTop: "#141530", canvasMid: "#111226", canvasBottom: "#0e0f21",
128
+ shadowInk: "#000000",
129
+ mint: "#1a4235", mint2: "#205241", mintTint: "#173a2f", mintInk: "#6ee8b3", forest: "#a8ecc9", green: "#4bdc98",
130
+ warm: "#46381a", warm2: "#54441e", warmInk: "#f5c94f", bronze: "#ffe08a", amber: "#f5b73a", honey: "#f5c974", spark: "#ffd166",
131
+ peach: "#3f2a1a", peach2: "#4a3020", orange: "#ff9a4d", orangeLight: "#ffab66", orangeDeep: "#f07c34", orangeDark: "#ffb36b", ember: "#ff8a55", emberDeep: "#f0603c",
132
+ rose: "#401c1e", rose2: "#4d2325", roseInk: "#ff7a70", roseDark: "#ff8f86", pink: "#ff8ac4", pinkTint: "#351a2a",
133
+ sky: "#1a2e46", blue: "#6aa4ff", blueDeep: "#8cb8ff", blueSoft: "#9fc4ff",
134
+ orchid: "#2e2140", orchidInk: "#c9a3ff", orchidMid: "#b48cf0",
135
+ periwinkle: "#34365c", periwinkleDeep: "#40437a",
136
+ deep: "#0f1020", deepGutter: "#0b0c1a", deepInk: "#f4f2ff", deepMuted: "#7c78ad", deepHead: "#e8e6ff", deepMeta: "#a9a5d8",
137
+ lavGrey: "#7c78ad", lilac: "#b9a7ff", seafoam: "#8fe3bd", azure: "#9cd2ff", salmon: "#ff9f9f", magenta: "#f2b8ff",
138
+ };
139
+
140
+ const clay = {
141
+ ...pop,
142
+ primary: "#d97757", primaryLight: "#e08a6d", primaryDeep: "#c4633f", primaryDark: "#a8502f",
143
+ violet: "#9d76c4",
144
+ iris: "#d97757",
145
+ ink: "#1f1e1d", ink2: "#3d3929", ink3: "#5e5a4f", indigo: "#2a2622",
146
+ muted: "#8a8577", faint: "#b3ada0", placeholder: "#a39e90", grey: "#9c988c",
147
+ white: "#ffffff", black: "#000000",
148
+ bg: "#f0eee6", soft: "#f7f5ef", softer: "#f3f0e8", lav: "#ebe7db", lav2: "#dfd9c8",
149
+ canvasTop: "#faf9f5", canvasMid: "#f5f3ec", canvasBottom: "#efece3",
150
+ shadowInk: "#4a3f2a",
151
+ mint: "#dff0e1", mint2: "#cfe6d2", mintTint: "#e8f3e9", mintInk: "#2f7a4f", forest: "#1f5a3a", green: "#4caf77",
152
+ warm: "#fbeecf", warm2: "#f6e2a8", warmInk: "#9a6b0f", bronze: "#5a4200", amber: "#d99a2b", honey: "#f2cc8f", spark: "#f0c96b",
153
+ peach: "#fbe3d3", peach2: "#f8d9c4", orange: "#e67e3c", orangeLight: "#ee9256", orangeDeep: "#cf6a2c", orangeDark: "#a8502f", ember: "#e07a4d", emberDeep: "#c9553a",
154
+ rose: "#f9e3df", rose2: "#f4d3cd", roseInk: "#c24d43", roseDark: "#a13d34", pink: "#b0356f", pinkTint: "#fbeef3",
155
+ sky: "#e0ecf5", blue: "#4a7fb5", blueDeep: "#35618c", blueSoft: "#7ea6d0",
156
+ orchid: "#ece3f2", orchidInk: "#7a4fa3", orchidMid: "#9d76c4",
157
+ periwinkle: "#d8d4e6", periwinkleDeep: "#bdb7d6",
158
+ deep: "#2b2622", deepGutter: "#241f1c", deepInk: "#f5f1e8", deepMuted: "#8c8477", deepHead: "#efe9dc", deepMeta: "#b8b0a0",
159
+ lavGrey: "#cfc8b8", lilac: "#c3b1ff", seafoam: "#9ad8b6", azure: "#a9cbe8", salmon: "#f2a79a", magenta: "#e5b3e0",
160
+ };
161
+
162
+ const comfort = {
163
+ ...pop,
164
+ primary: "#2f6f68", primaryLight: "#3d7f78", primaryDeep: "#275e58", primaryDark: "#1f4d48",
165
+ violet: "#5e6b8c",
166
+ iris: "#2f6f68",
167
+ ink: "#2b2a27", ink2: "#3a3833", ink3: "#5a574f", indigo: "#2b2a27",
168
+ muted: "#6f6b62", faint: "#8d887c", placeholder: "#9a958a", grey: "#8f8b82",
169
+ white: "#f9f6ef", black: "#000000",
170
+ bg: "#efe9dd", soft: "#f4efe5", softer: "#f1ebe0", lav: "#e8e1d3", lav2: "#dcd3c2",
171
+ canvasTop: "#f2ede3", canvasMid: "#efe9dd", canvasBottom: "#ebe4d6",
172
+ shadowInk: "#4a4438",
173
+ mint: "#e0ebe2", mint2: "#d1e2d5", mintTint: "#e8f0ea", mintInk: "#2f6b4f", forest: "#244f3c", green: "#4f9a72",
174
+ warm: "#f4e9cf", warm2: "#ecdfb9", warmInk: "#7d5f12", bronze: "#5a4200", amber: "#c99a3c", honey: "#7d5f12", spark: "#c9a04f",
175
+ peach: "#f3e2d3", peach2: "#efdac8", orange: "#c2703f", orangeLight: "#cd7f50", orangeDeep: "#a85c30", orangeDark: "#8a4a26", ember: "#b8623a", emberDeep: "#9c4a2a",
176
+ rose: "#f2e0dc", rose2: "#ead2cc", roseInk: "#a34a3f", roseDark: "#8a3d34", pink: "#8f3d6a", pinkTint: "#f2e3ea",
177
+ sky: "#dfe7ee", blue: "#3f6e9a", blueDeep: "#2f587d", blueSoft: "#6f93b8",
178
+ orchid: "#e8e2ec", orchidInk: "#6b4f8c", orchidMid: "#8a6fa8",
179
+ periwinkle: "#d9dbe6", periwinkleDeep: "#b9bdd0",
180
+ deep: "#e9e3d6", deepGutter: "#e1dacb", deepInk: "#2b2a27", deepMuted: "#7a7468", deepHead: "#3a3833", deepMeta: "#6f6b62",
181
+ lavGrey: "#7a7468", lilac: "#5b4a9a", seafoam: "#1f6e5a", azure: "#2a5d8f", salmon: "#a3472f", magenta: "#8a3f7a",
182
+ };
183
+
184
+ const plush = {
185
+ ...pop,
186
+ primary: "#917ad8", primaryLight: "#b4a2e6", primaryDeep: "#7f66cc", primaryDark: "#6a54b4",
187
+ violet: "#c9b8f0",
188
+ iris: "#917ad8",
189
+ ink: "#5a3e36", ink2: "#634840", ink3: "#8a6f64", indigo: "#5a3e36",
190
+ muted: "#8f7a6e", faint: "#b3a094", placeholder: "#b8a498", grey: "#b3a094",
191
+ white: "#f9f0e1", black: "#000000",
192
+ bg: "#f4e8ce", soft: "#fdf7ec", softer: "#f4e9d8", lav: "#f1e4d2", lav2: "#e8d7c1",
193
+ canvasTop: "#fbf3e6", canvasMid: "#f7ecdd", canvasBottom: "#f1e5ec",
194
+ shadowInk: "#7a5540",
195
+ mint: "#c9e8c4", mint2: "#b4dfae", mintTint: "#dff1dc", mintInk: "#3f7a4a", forest: "#2f6038", green: "#6fcf7a",
196
+ warm: "#fbe4a4", warm2: "#f7d774", warmInk: "#8a6a1c", bronze: "#5a4200", amber: "#f0c24a", honey: "#f7d774", spark: "#fbe07a",
197
+ peach: "#f9dcc8", peach2: "#f5cbae", orange: "#f0a06a", orangeLight: "#f4b385", orangeDeep: "#e08a50", orangeDark: "#b8673a", ember: "#ec6f9d", emberDeep: "#d8598a",
198
+ rose: "#f7c9d8", rose2: "#f2b3c9", roseInk: "#c2456f", roseDark: "#a63c64", pink: "#d85a8c", pinkTint: "#fbe3ec",
199
+ sky: "#d5f0ec", blue: "#5fbdb3", blueDeep: "#33857c", blueSoft: "#8fd6cf",
200
+ orchid: "#e6dff7", orchidInk: "#6a55b0", orchidMid: "#a893e2",
201
+ periwinkle: "#ded5f3", periwinkleDeep: "#c5b8ea",
202
+ deep: "#4a352e", deepGutter: "#3f2d27", deepInk: "#f9f1e4", deepMuted: "#a58f83", deepHead: "#f0e4d4", deepMeta: "#c2ada0",
203
+ lavGrey: "#cdbfb0", lilac: "#c9b8f0", seafoam: "#8fd6cf", azure: "#9ccbe8", salmon: "#f4b385", magenta: "#f0a0c0",
204
+ };
205
+
95
206
  const meaningOf = (p) => ({
96
207
  stReady: p.mint, stReadyInk: p.mintInk, stReadyDot: p.green,
97
208
  stWaiting: p.sky, stWaitingInk: p.blueDeep, stWaitingDot: p.blueSoft,
@@ -111,8 +222,6 @@
111
222
  tickMine: p.periwinkle,
112
223
  tickMineView: p.periwinkleDeep,
113
224
  tickFallback: p.grey,
114
- unseenInk: p.pink,
115
- unseenBg: p.pinkTint,
116
225
  });
117
226
 
118
227
  const aliasesOf = (p) => ({
@@ -161,6 +270,9 @@
161
270
  shadowInset: "none",
162
271
  shadowSoft: edge,
163
272
  shadow: edge,
273
+ bevel: "none",
274
+ shadowRoom: "0px",
275
+ bevelPressed: "none",
164
276
  };
165
277
  };
166
278
 
@@ -174,12 +286,46 @@
174
286
  sbThumbHover: alpha(p.primary, 0.45),
175
287
  });
176
288
 
289
+ const fonts = {
290
+ text: {
291
+ nunito: { label: "Nunito (default)", stack: '"Nunito", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
292
+ inter: { label: "Inter", stack: '"Inter", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
293
+ "noto-sans": { label: "Noto Sans", stack: '"Noto Sans", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
294
+ "open-sans": { label: "Open Sans", stack: '"Open Sans", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
295
+ "source-sans-3": { label: "Source Sans 3", stack: '"Source Sans 3", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
296
+ "ibm-plex-sans": { label: "IBM Plex Sans", stack: '"IBM Plex Sans", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
297
+ manrope: { label: "Manrope", stack: '"Manrope", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
298
+ rubik: { label: "Rubik", stack: '"Rubik", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
299
+ montserrat: { label: "Montserrat", stack: '"Montserrat", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
300
+ "golos-text": { label: "Golos Text", stack: '"Golos Text", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
301
+ "exo-2": { label: "Exo 2", stack: '"Exo 2", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
302
+ comfortaa: { label: "Comfortaa", stack: '"Comfortaa", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
303
+ "ubuntu-sans": { label: "Ubuntu Sans", stack: '"Ubuntu Sans", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
304
+ arial: { label: "Arial / Helvetica (system)", stack: 'Arial, Helvetica, "Liberation Sans", sans-serif' },
305
+ system: { label: "System UI font", stack: 'system-ui, -apple-system, "Segoe UI", Roboto, Cantarell, sans-serif' },
306
+ },
307
+ mono: {
308
+ "jetbrains-mono": { label: "JetBrains Mono (default)", stack: '"JetBrains Mono", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
309
+ "fira-code": { label: "Fira Code", stack: '"Fira Code", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
310
+ "source-code-pro": { label: "Source Code Pro", stack: '"Source Code Pro", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
311
+ "ibm-plex-mono": { label: "IBM Plex Mono", stack: '"IBM Plex Mono", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
312
+ "pt-mono": { label: "PT Mono", stack: '"PT Mono", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
313
+ "victor-mono": { label: "Victor Mono", stack: '"Victor Mono", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
314
+ "anonymous-pro": { label: "Anonymous Pro", stack: '"Anonymous Pro", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
315
+ "cascadia-code": { label: "Cascadia Code", stack: '"Cascadia Code", ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", monospace' },
316
+ system: { label: "System monospace", stack: 'ui-monospace, Consolas, Menlo, "DejaVu Sans Mono", "Courier New", monospace' },
317
+ },
318
+ };
319
+
177
320
  const shape = {
178
- rXs: "8px", rSm: "12px", rMd: "14px", rLg: "18px", rXl: "18px", rPill: "99px",
321
+ rScale: "1",
322
+ rCtlMin: "0px",
323
+ rXs: "calc(8px * var(--r-scale))", rSm: "calc(12px * var(--r-scale))", rMd: "calc(14px * var(--r-scale))", rLg: "calc(18px * var(--r-scale))", rXl: "calc(18px * var(--r-scale))", rPill: "calc(99px * var(--r-scale))",
179
324
  };
180
325
  const type = {
181
326
  font: '"Nunito", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif',
182
327
  mono: '"JetBrains Mono", ui-monospace, Consolas, "Courier New", monospace',
328
+ lineHeight: "1.45",
183
329
  fsScale: "1",
184
330
  fsXs: "calc(11px * var(--fs-scale))",
185
331
  fsSm: "calc(12px * var(--fs-scale))",
@@ -194,7 +340,9 @@
194
340
  easePop: "cubic-bezier(0.34, 1.56, 0.64, 1)",
195
341
  };
196
342
 
197
- const elementsOf = (p) => ({
343
+ const elementsOf = (p) => {
344
+ const dangerSolid = `0 8px 18px -8px ${alpha(p.roseInk, 0.8)}`;
345
+ return {
198
346
  page: {
199
347
  bg: p.bg,
200
348
  ink: p.ink,
@@ -214,6 +362,7 @@
214
362
  hoverInk: p.primary,
215
363
  onPrimary: p.white,
216
364
  secondaryBg: p.white,
365
+ secondaryHoverBg: p.white,
217
366
  softBg: p.lav,
218
367
  softHoverBg: p.lav2,
219
368
  ghostInk: p.muted,
@@ -223,14 +372,14 @@
223
372
  dangerHoverBg: p.rose2,
224
373
  dangerSolidInk: p.white,
225
374
  dangerSolidHoverBg: p.roseDark,
226
- dangerSolidShadow: `0 8px 18px -8px ${alpha(p.roseInk, 0.8)}`,
375
+ dangerSolidShadow: dangerSolid,
227
376
  spinnerTrack: alpha(p.white, 0.5),
228
377
  spinnerHead: p.white,
229
378
  spinnerTrackQuiet: p.lav2,
230
379
  spinnerHeadQuiet: p.primary,
231
380
  disabledOpacity: "0.45",
232
- darkBg: alpha(p.white, 0.12),
233
- darkInk: p.white,
381
+ darkBg: alpha(p.deepInk, 0.12),
382
+ darkInk: p.deepInk,
234
383
  darkHoverBg: p.primary,
235
384
  warnInk: p.warmInk,
236
385
  warnHoverBg: p.warm,
@@ -244,6 +393,23 @@
244
393
  paperBg: p.white,
245
394
  paperInk: p.ink3,
246
395
  paperHoverBg: p.soft,
396
+ border: "0",
397
+ case: "none",
398
+ tracking: "0px",
399
+ linkUnderline: "none",
400
+ ghostBg: "transparent",
401
+ shadow: "none",
402
+ hoverShadow: "none",
403
+ hoverLift: "0px",
404
+ activeShadow: "none",
405
+ primaryActiveShadow: "var(--shadow-primary)",
406
+ primaryHoverFilter: "brightness(1.05)",
407
+ secondaryHoverShadow: "var(--shadow-tile)",
408
+ secondaryActiveShadow: "var(--shadow-tile)",
409
+ dangerSolidHoverShadow: dangerSolid,
410
+ dangerSolidActiveShadow: dangerSolid,
411
+ okShadow: "none",
412
+ warnShadow: "none",
247
413
  h: "42px",
248
414
  hSm: "34px",
249
415
  hXs: "24px",
@@ -272,6 +438,14 @@
272
438
  inlineInk: p.ink3,
273
439
  inlineOnBg: alpha(p.primary, 0.1),
274
440
  inlineOnInk: p.primary,
441
+ ghostBg: "transparent",
442
+ shadow: "none",
443
+ hoverShadow: "none",
444
+ hoverLift: "0px",
445
+ activeShadow: "none",
446
+ primaryShadow: "var(--shadow-primary)",
447
+ primaryHoverShadow: "var(--shadow-primary)",
448
+ primaryActiveShadow: "var(--shadow-primary)",
275
449
  },
276
450
  badge: {
277
451
  bg: p.lav,
@@ -281,23 +455,37 @@
281
455
  mutedBg: p.softer,
282
456
  mutedInk: p.muted,
283
457
  outlineBorder: p.lav2,
458
+ outlineW: "2px",
284
459
  outlineInk: p.muted,
285
460
  fsXs: "calc(10px * var(--fs-scale))",
461
+ fill: "100%",
462
+ border: "0",
463
+ case: "none",
464
+ tracking: "0px",
465
+ shadow: "none",
286
466
  },
287
467
  chip: {
288
468
  bg: p.lav,
289
469
  ink: p.primary,
290
470
  hoverBg: p.lav2,
471
+ fill: "100%",
472
+ border: "0",
473
+ shadow: "none",
474
+ hoverShadow: "none",
475
+ hoverLift: "0px",
476
+ activeShadow: "none",
477
+ activeDrop: "0px",
291
478
  },
292
479
  fileCard: {
293
480
  bg: p.deep,
294
481
  ink: p.deepInk,
295
- headBg: alpha(p.white, 0.06),
482
+ headBg: alpha(p.deepInk, 0.06),
296
483
  headInk: p.deepHead,
297
484
  headMeta: p.deepMeta,
298
485
  noteInk: p.roseInk,
299
- imageBodyBg: alpha(p.white, 0.04),
486
+ imageBodyBg: alpha(p.deepInk, 0.04),
300
487
  imageBg: p.white,
488
+ shadow: "var(--edge)",
301
489
  },
302
490
  toolCall: {
303
491
  readyBg: p.mint,
@@ -323,6 +511,7 @@
323
511
  hoverInk: p.primary,
324
512
  failedInk: p.roseInk,
325
513
  fs: "calc(11px * var(--fs-scale))",
514
+ shadow: "none",
326
515
  },
327
516
  askCard: {
328
517
  bg: p.warm,
@@ -333,6 +522,7 @@
333
522
  kindFs: "calc(11px * var(--fs-scale))",
334
523
  inputFs: "calc(11px * var(--fs-scale))",
335
524
  outcomeFs: "calc(12px * var(--fs-scale))",
525
+ shadow: "none",
336
526
  },
337
527
  replyNote: {
338
528
  ink: p.muted,
@@ -341,6 +531,16 @@
341
531
  attentionBg: p.warm,
342
532
  fs: "calc(12px * var(--fs-scale))",
343
533
  },
534
+ lookCard: {
535
+ w: "150px",
536
+ paperH: "84px",
537
+ bg: p.white,
538
+ ink: p.ink,
539
+ border: p.lav2,
540
+ hoverBorder: p.periwinkleDeep,
541
+ onBorder: p.primary,
542
+ shadow: "none",
543
+ },
344
544
  choice: {
345
545
  bg: p.soft,
346
546
  ink: p.ink3,
@@ -349,6 +549,13 @@
349
549
  onBorder: p.primary,
350
550
  quietInk: p.muted,
351
551
  h: "32px",
552
+ border: "transparent",
553
+ shadow: "none",
554
+ hoverShadow: "none",
555
+ hoverLift: "0px",
556
+ activeShadow: "none",
557
+ activeDrop: "0px",
558
+ onShadow: "none",
352
559
  },
353
560
  control: {
354
561
  knob: p.white,
@@ -357,18 +564,19 @@
357
564
  tick: p.white,
358
565
  },
359
566
  tile: {
360
- logoBg: p.white,
361
567
  badgeBg: p.white,
362
- badgeBorder: p.white,
363
- ring: p.white,
364
- markInk: p.white,
365
568
  countInk: p.white,
366
569
  },
367
570
  roomMark: {
571
+ ink: p.white,
368
572
  gradAngle: "135deg",
369
573
  gradFrom: "72% 66%",
370
574
  gradTo: "68% 52%",
371
575
  emoji: "70% 93%",
576
+ shadow: "none",
577
+ radius: "calc(14px * var(--r-scale))",
578
+ radiusLg: "calc(16px * var(--r-scale))",
579
+ radiusRail: "calc(13px * var(--r-scale))",
372
580
  },
373
581
  pop: {
374
582
  bg: p.white,
@@ -388,7 +596,7 @@
388
596
  },
389
597
  toast: {
390
598
  bg: p.ink,
391
- ink: p.deepInk,
599
+ ink: p.white,
392
600
  },
393
601
  rail: {
394
602
  logoShadow: `0 8px 18px -6px ${alpha(p.primary, 0.6)}`,
@@ -405,6 +613,7 @@
405
613
  bubble: {
406
614
  bg: p.white,
407
615
  ink: p.ink2,
616
+ border: "transparent",
408
617
  shadow: `0 2px 0 ${alpha(p.ink, 0.06)}, 0 1px 3px ${alpha(p.ink, 0.04)}`,
409
618
  mineBg: `linear-gradient(135deg, ${p.primaryLight}, ${p.primaryDeep})`,
410
619
  mineInk: p.white,
@@ -454,21 +663,21 @@
454
663
  gutterInk: p.deepMuted,
455
664
  lineMarkInk: p.honey,
456
665
  lineMarkBg: alpha(p.honey, 0.16),
457
- headBg: alpha(p.white, 0.06),
666
+ headBg: alpha(p.deepInk, 0.06),
458
667
  headInk: p.deepHead,
459
668
  headMeta: p.deepMeta,
460
- headBtnBg: alpha(p.white, 0.12),
461
- headBtnInk: p.white,
462
- bodyBg: alpha(p.white, 0.04),
669
+ headBtnBg: alpha(p.deepInk, 0.12),
670
+ headBtnInk: p.deepInk,
671
+ bodyBg: alpha(p.deepInk, 0.04),
463
672
  sbTrack: alpha(p.black, 0.25),
464
- sbThumb: alpha(p.white, 0.3),
465
- sbThumbHover: alpha(p.white, 0.5),
466
- sbFirefox: alpha(p.white, 0.35),
673
+ sbThumb: alpha(p.deepInk, 0.3),
674
+ sbThumbHover: alpha(p.deepInk, 0.5),
675
+ sbFirefox: alpha(p.deepInk, 0.35),
467
676
  findHitBg: p.honey,
468
677
  findHitInk: p.deep,
469
678
  },
470
679
  syntax: {
471
- comment: p.muted,
680
+ comment: p.deepMuted,
472
681
  punctuation: p.lavGrey,
473
682
  keyword: p.lilac,
474
683
  string: p.seafoam,
@@ -486,29 +695,81 @@
486
695
  input: {
487
696
  bg: p.white,
488
697
  focusBorder: p.primary,
698
+ shadow: "none",
489
699
  },
490
700
  profile: {
491
701
  bg: `linear-gradient(160deg, ${p.lav}, ${p.mintTint})`,
492
- avatarBg: p.white,
493
702
  badgeBg: p.white,
494
703
  },
495
- participant: {
704
+ face: {
705
+ paper: p.white,
706
+ tint: "16%",
707
+ corner: "0.32",
708
+ ring: p.white,
709
+ meRing: p.primary,
710
+ dotBg: p.faint,
711
+ cardBg: p.white,
496
712
  fallback: p.grey,
497
713
  humanInk: p.indigo,
498
714
  avatarDefault: p.primary,
499
715
  avatarGloss: p.white,
500
716
  avatarLabelInk: p.white,
501
- avatarBlend: p.white,
717
+ shadow: NO_SHADOW,
718
+ cardShadow: "var(--shadow-pop)",
719
+ },
720
+ logoTile: {
721
+ bg: p.white,
722
+ ink: p.indigo,
723
+ badgeBg: p.white,
724
+ badgeBorder: p.white,
725
+ badgeInk: p.indigo,
726
+ mutedBg: p.warm,
727
+ mutedInk: p.warmInk,
728
+ sm: "30px",
729
+ md: "36px",
730
+ lg: "40px",
731
+ shadow: "var(--shadow-tile)",
732
+ radius: "calc(12px * var(--r-scale))",
733
+ radiusSm: "calc(10px * var(--r-scale))",
734
+ radiusLg: "calc(13px * var(--r-scale))",
735
+ radiusBadge: "calc(6px * var(--r-scale))",
736
+ },
737
+ unseenLine: {
738
+ ink: p.pink,
739
+ bg: p.pinkTint,
740
+ line: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='8' viewBox='0 0 16 8'><path d='M0 4 Q4 0 8 4 T16 4' fill='none' stroke='${p.pink.replace("#", "%23")}' stroke-width='1.6'/></svg>") repeat-x center / 16px 8px`,
741
+ lineH: "8px",
742
+ lineOpacity: "0.55",
743
+ labelBorder: "0",
744
+ labelCase: "none",
745
+ labelTracking: "0px",
746
+ labelShadow: "none",
747
+ },
748
+ numberField: {
749
+ stepW: "22px",
750
+ stepInk: p.muted,
751
+ stepHoverBg: p.lav,
752
+ stepHoverInk: p.primary,
753
+ },
754
+ adjustRow: {
755
+ swatchBorder: p.lav2,
756
+ valueInk: p.muted,
757
+ hintInk: p.muted,
758
+ changedInk: p.primary,
759
+ backInk: p.muted,
760
+ backHoverBg: p.lav,
502
761
  },
503
762
  rowButton: {
504
763
  size: "24px",
505
764
  icon: "14px",
506
- radius: "8px",
765
+ radius: "calc(8px * var(--r-scale))",
507
766
  bg: p.softer,
508
767
  ink: p.muted,
509
768
  hoverBg: p.primary,
510
769
  hoverInk: p.white,
511
770
  disabledOpacity: "0.4",
771
+ shadow: "none",
772
+ activeShadow: "none",
512
773
  },
513
774
  hubRow: {
514
775
  ink: p.muted,
@@ -518,6 +779,9 @@
518
779
  actionInk: p.mintInk,
519
780
  actionHoverBg: p.mint2,
520
781
  faceSize: "18px",
782
+ prefix: "none",
783
+ prefixGap: "0px",
784
+ actionShadow: "var(--edge)",
521
785
  },
522
786
  hero: {
523
787
  ink: p.white,
@@ -541,6 +805,13 @@
541
805
  feature: {
542
806
  emojiBg: alpha(p.white, 0.8),
543
807
  peachBg: p.peach2,
808
+ shadow: "var(--edge)",
809
+ },
810
+ composer: {
811
+ shadow: "none",
812
+ bg: "transparent",
813
+ radius: "0px",
814
+ padX: "0px",
544
815
  },
545
816
  step: {
546
817
  bg: p.white,
@@ -553,7 +824,8 @@
553
824
  browser: {
554
825
  themeColor: p.iris,
555
826
  },
556
- });
827
+ };
828
+ };
557
829
 
558
830
  const popNodes = [
559
831
  { fill: "#e4e6fb", stroke: "#5b5bf0" },
@@ -575,22 +847,133 @@
575
847
  },
576
848
  };
577
849
 
578
- const look = (id, label, scheme, palette) => ({
579
- id,
580
- label,
581
- scheme,
582
- palette,
583
- meaning: meaningOf(palette),
584
- aliases: aliasesOf(palette),
585
- elevation: elevationOf(palette),
586
- canvas: canvasOf(palette),
587
- shape,
588
- type,
589
- motion,
590
- elements: elementsOf(palette),
591
- });
850
+ const look = (id, label, scheme, palette, wants = {}) => {
851
+ const elements = elementsOf(palette);
852
+ for (const [group, parts] of Object.entries(wants.elements ? wants.elements(palette) : {})) elements[group] = { ...(elements[group] || {}), ...parts };
853
+ return {
854
+ id,
855
+ label,
856
+ scheme,
857
+ palette,
858
+ meaning: meaningOf(palette),
859
+ aliases: aliasesOf(palette),
860
+ elevation: { ...elevationOf(palette), ...(wants.elevation || {}) },
861
+ canvas: { ...canvasOf(palette), ...(wants.canvas ? wants.canvas(palette) : {}) },
862
+ shape: { ...shape, ...(wants.shape || {}) },
863
+ type: { ...type, ...(wants.type || {}) },
864
+ motion: { ...motion, ...(wants.motion || {}) },
865
+ elements,
866
+ wants,
867
+ custom: false,
868
+ };
869
+ };
870
+
871
+ const flat = Object.fromEntries(Object.keys(elevationOf(pop)).map((key) => [key, "none"]));
592
872
 
593
- const looks = { pop: look("pop", "Pop", "light", pop) };
873
+ const looks = {
874
+ classic: look("classic", "VibeClassic", "light", pop),
875
+ "classic-dark": look("classic-dark", "VibeClassic Dark", "dark", classicDark, {
876
+ elements: (p) => ({ logoTile: { bg: p.lav, ink: p.ink, badgeBg: p.lav, badgeInk: p.ink }, bubble: { border: mix(p.white, p.ink, 0.14) } }),
877
+ }),
878
+ clay: look("clay", "Clay", "light", clay, {
879
+ type: { font: '"Inter", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif' },
880
+ canvas: (p) => ({ gradCanvas: p.bg, canvasPattern: "none", canvasPatternSize: "0 0" }),
881
+ elements: (p) => ({ btn: { hoverInk: p.primaryDeep }, iconButton: { hoverInk: p.primaryDeep }, badge: { ink: p.primaryDeep }, chip: { ink: p.primaryDeep }, toolCall: { pendingInk: p.primaryDeep }, toolFold: { hoverInk: p.primaryDeep }, numberField: { stepHoverInk: p.primaryDeep } }),
882
+ }),
883
+ comfort: look("comfort", "Eye Comfort", "light", comfort, {
884
+ type: { font: '"Open Sans", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif', lineHeight: "1.6" },
885
+ canvas: (p) => ({ gradCanvas: p.bg, canvasPattern: "none", canvasPatternSize: "0 0" }),
886
+ elevation: { shadowPrimary: "none", shadowGlow: "none" },
887
+ elements: (p) => ({
888
+ code: { headBg: alpha(p.ink, 0.04), headBtnBg: alpha(p.ink, 0.08), headBtnInk: p.ink, bodyBg: alpha(p.ink, 0.02), sbTrack: alpha(p.ink, 0.06), sbThumb: alpha(p.ink, 0.3) },
889
+ bubble: { mineBg: `linear-gradient(135deg, ${p.primaryLight}, ${p.primaryDeep})`, shadow: `0 1px 2px ${alpha(p.ink, 0.06)}`, mineShadow: "none" },
890
+ logoTile: { bg: p.white, ink: p.ink },
891
+ btn: { darkHoverBg: p.lav2 },
892
+ }),
893
+ }),
894
+ plush: (() => {
895
+ const p = plush;
896
+ const warm = (a) => alpha(p.shadowInk, a);
897
+ const light = (a) => alpha(p.white, a);
898
+ const dark = (a) => alpha(p.black, a);
899
+ const puff = `inset 0 1.5px 2px ${light(0.95)}, inset 1px 0 1px ${light(0.5)}, inset 0 -4px 5px -2px ${warm(0.2)}, inset -1px 0 2px -1px ${warm(0.08)}, 0 0 0 1px ${warm(0.07)}, 0 1px 2px ${warm(0.14)}, 0 6px 12px -4px ${warm(0.3)}`;
900
+ const tinted = `inset 0 1.5px 2px ${light(0.6)}, inset 1px 0 1px ${light(0.35)}, inset 0 -4px 5px -2px ${dark(0.16)}, inset -1px 0 2px -1px ${dark(0.06)}, 0 0 0 1px ${warm(0.08)}, 0 1px 2px ${warm(0.16)}, 0 6px 12px -4px ${warm(0.32)}`;
901
+ const panel = `inset 0 2px 4px ${light(0.95)}, inset 0 -8px 12px -3px ${warm(0.18)}, inset 2px 0 3px -1px ${light(0.5)}, 0 3px 6px ${warm(0.1)}, 0 16px 32px -8px ${warm(0.28)}`;
902
+ const frame = `inset 0 2px 4px ${light(0.95)}, inset 0 -10px 14px -4px ${warm(0.2)}, 0 6px 10px ${warm(0.12)}, 0 30px 60px -16px ${warm(0.4)}`;
903
+ const sunk = `inset 0 3px 6px ${warm(0.18)}, inset 0 -1px 2px ${light(0.8)}`;
904
+ const lifted = `inset 0 1.5px 2px ${light(0.95)}, inset 1px 0 1px ${light(0.5)}, inset 0 -4px 5px -2px ${warm(0.2)}, inset -1px 0 2px -1px ${warm(0.08)}, 0 0 0 1px ${warm(0.07)}, 0 2px 4px ${warm(0.16)}, 0 10px 18px -5px ${warm(0.34)}`;
905
+ return look("plush", "3D clayful", "light", plush, {
906
+ shape: { rScale: "1.6", rCtlMin: "var(--r-pill)" },
907
+ canvas: (p) => ({
908
+ gradCanvas: `radial-gradient(720px 480px at 100% 100%, ${alpha(p.ember, 0.14)}, transparent 70%), radial-gradient(640px 420px at 0% 0%, ${alpha(p.primaryLight, 0.16)}, transparent 70%), linear-gradient(165deg, ${p.canvasTop} 0%, ${p.canvasMid} 55%, ${p.canvasBottom} 100%)`,
909
+ canvasPattern: "none",
910
+ canvasPatternSize: "0 0",
911
+ }),
912
+ elevation: {
913
+ edge: panel, shadowTile: puff, shadowPop: panel, shadowPrimary: tinted, shadowDialog: frame,
914
+ shadowInset: sunk, shadowCtl: sunk, shadowCtlHover: lifted, shadowCard: panel, shadowSoft: tinted,
915
+ shadow: puff, shadow1: panel, shadow2: frame, shadowGlow: tinted,
916
+ bevel: `linear-gradient(180deg, ${light(0.3)} 0%, ${light(0)} 45%)`,
917
+ shadowRoom: "20px",
918
+ },
919
+ elements: (p) => ({
920
+ bubble: {
921
+ shadow: `inset 0 2px 3px ${alpha(p.white, 0.95)}, inset 2px 0 2px -1px ${alpha(p.white, 0.6)}, inset 0 -6px 8px -2px ${alpha(p.shadowInk, 0.2)}, inset -2px 0 4px -2px ${alpha(p.shadowInk, 0.1)}, 0 2px 4px ${alpha(p.shadowInk, 0.12)}, 0 10px 18px -4px ${alpha(p.shadowInk, 0.26)}`,
922
+ bg: p.soft,
923
+ mineBg: `linear-gradient(${p.primary}, ${p.primary})`,
924
+ mineCtaInk: p.primaryDark,
925
+ mineShadow: `inset 0 2px 3px ${alpha(p.white, 0.6)}, inset 2px 0 2px -1px ${alpha(p.white, 0.35)}, inset 0 -6px 8px -2px ${alpha(p.black, 0.16)}, inset -2px 0 4px -2px ${alpha(p.black, 0.06)}, 0 2px 4px ${alpha(p.shadowInk, 0.14)}, 0 10px 18px -4px ${alpha(p.shadowInk, 0.3)}`,
926
+ },
927
+ input: { shadow: sunk },
928
+ composer: { shadow: sunk, bg: p.softer, radius: "calc(14px * var(--r-scale))", padX: "12px" },
929
+ logoTile: { bg: p.soft, badgeBg: p.soft, shadow: puff, radius: "calc(8px * var(--r-scale))", radiusSm: "calc(7px * var(--r-scale))", radiusLg: "calc(9px * var(--r-scale))", radiusBadge: "calc(8px * var(--r-scale))" },
930
+ face: { tint: "34%", corner: "0.2", shadow: tinted, cardShadow: tinted },
931
+ btn: {
932
+ hoverInk: p.primaryDark,
933
+ shadow: puff, hoverShadow: lifted, hoverLift: "-1px", activeShadow: sunk,
934
+ primaryActiveShadow: sunk, primaryHoverFilter: "none",
935
+ secondaryHoverShadow: lifted, secondaryActiveShadow: sunk,
936
+ dangerSolidShadow: tinted, dangerSolidHoverShadow: lifted, dangerSolidActiveShadow: sunk,
937
+ okShadow: tinted, warnShadow: tinted,
938
+ ghostBg: p.softer, softBg: p.softer, secondaryBg: p.softer, paperBg: p.softer,
939
+ },
940
+ iconButton: { hoverInk: p.primaryDark, ghostInk: p.muted, shadow: puff, hoverShadow: lifted, hoverLift: "-1px", activeShadow: sunk, primaryShadow: puff, primaryHoverShadow: lifted, primaryActiveShadow: sunk, ghostBg: p.softer },
941
+ rowButton: { shadow: puff, activeShadow: sunk, radius: "8px" },
942
+ badge: { ink: p.primaryDark, shadow: tinted },
943
+ chip: { ink: p.primaryDark, shadow: tinted, hoverShadow: lifted, hoverLift: "-1px", activeShadow: sunk, activeDrop: "1px" },
944
+ choice: { shadow: tinted, hoverShadow: lifted, hoverLift: "-1px", activeShadow: sunk, activeDrop: "1px", onShadow: sunk },
945
+ hubRow: { actionShadow: tinted },
946
+ unseenLine: { labelShadow: tinted },
947
+ lookCard: { shadow: puff },
948
+ fileCard: { shadow: puff },
949
+ askCard: { shadow: puff },
950
+ toolFold: { hoverInk: p.primaryDark, shadow: puff },
951
+ roomMark: { shadow: tinted, radius: "calc(10px * var(--r-scale))", radiusLg: "calc(12px * var(--r-scale))", radiusRail: "calc(10px * var(--r-scale))" },
952
+ feature: { shadow: panel },
953
+ toolCall: { pendingInk: p.primaryDark }, numberField: { stepHoverInk: p.primaryDark },
954
+ }),
955
+ });
956
+ })(),
957
+ terminal: look("terminal", "Terminal", "dark", terminal, {
958
+ shape: { rScale: "0" },
959
+ type: { font: type.mono },
960
+ motion: { tFast: "0ms", tBase: "0ms", tSlow: "0ms" },
961
+ elevation: flat,
962
+ canvas: (p) => ({ gradCanvas: p.bg, canvasPattern: "none", canvasPatternSize: "0 0" }),
963
+ elements: (p) => ({
964
+ roomMark: { ink: p.ink, gradFrom: "38% 24%", gradTo: "34% 18%", emoji: "35% 20%" },
965
+ tile: { badgeBg: p.lav2 },
966
+ logoTile: { bg: p.lav2, ink: p.primary, badgeBg: p.lav2, badgeBorder: p.lav2, badgeInk: p.primary },
967
+ bubble: { border: p.lav2, shadow: NO_SHADOW, mineShadow: NO_SHADOW },
968
+ btn: { border: "1px solid currentColor", case: "uppercase", tracking: "0.06em", linkUnderline: "underline" },
969
+ badge: { fill: "0%", border: "1px solid currentColor", case: "uppercase", tracking: "0.04em", outlineBorder: "currentColor", outlineW: "1px" },
970
+ chip: { fill: "0%", border: "1px solid currentColor" },
971
+ choice: { border: "currentColor", onBorder: "currentColor" },
972
+ hubRow: { prefix: '">"', prefixGap: "6px" },
973
+ unseenLine: { ink: p.muted, bg: "transparent", line: `repeating-linear-gradient(90deg, ${p.lav2} 0 6px, transparent 6px 12px)`, lineH: "1px", lineOpacity: "1", labelBorder: "1px solid currentColor", labelCase: "uppercase", labelTracking: "0.04em" },
974
+ }),
975
+ }),
976
+ };
594
977
 
595
978
  const FLAT = ["palette", "meaning", "aliases", "elevation", "canvas", "shape", "type", "motion"];
596
979
  const kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([a-zA-Z])(\d)/g, "$1-$2").toLowerCase();
@@ -616,5 +999,377 @@
616
999
  return Object.fromEntries(cssGroups(theme).flatMap((g) => g.entries));
617
1000
  }
618
1001
 
619
- globalThis.VIBEROOM_TOKENS = { looks, current: looks.pop, diagrams, cssGroups, cssVars, alpha, mix, kebab };
1002
+ const active = () => looks[globalThis.document?.documentElement?.dataset?.look] || looks.classic;
1003
+
1004
+ const toHsl = (hex) => {
1005
+ const [r, g, b] = rgb(hex).map((v) => v / 255);
1006
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), l = (max + min) / 2;
1007
+ if (max === min) return [0, 0, l];
1008
+ const d = max - min;
1009
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1010
+ const h = max === r ? ((g - b) / d + (g < b ? 6 : 0)) : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
1011
+ return [h * 60, s, l];
1012
+ };
1013
+ const fromHsl = (h, s, l) => {
1014
+ const k = (n) => (n + h / 30) % 12;
1015
+ const a = s * Math.min(l, 1 - l);
1016
+ const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
1017
+ return `#${[f(0), f(8), f(4)].map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("")}`;
1018
+ };
1019
+ const deepFill = (hex) => { const [h, s] = toHsl(hex); return fromHsl(h, s < 0.1 ? 0 : Math.min(0.45, Math.max(0.25, s)), 0.24); };
1020
+ const litLine = (hex) => { const [h, s, l] = toHsl(hex); return fromHsl(h, s, Math.max(l, 0.62)); };
1021
+ const DIAGRAM_FILLS = ["primaryColor", "secondaryColor", "tertiaryColor", "clusterBkg", "noteBkgColor", "edgeLabelBackground"];
1022
+ const DIAGRAM_INKS = ["primaryTextColor", "textColor"];
1023
+ const DIAGRAM_LINES = ["lineColor"];
1024
+ diagrams.forScheme = (preset, scheme, ink) => {
1025
+ if (scheme !== "dark") return preset;
1026
+ const out = { ...preset };
1027
+ for (const key of DIAGRAM_FILLS) if (out[key]) out[key] = deepFill(out[key]);
1028
+ for (const key of DIAGRAM_INKS) if (out[key]) out[key] = ink;
1029
+ for (const key of DIAGRAM_LINES) if (out[key]) out[key] = litLine(out[key]);
1030
+ if (out.palette) out.palette = out.palette.map((c) => ({ fill: deepFill(c.fill), stroke: litLine(c.stroke) }));
1031
+ return out;
1032
+ };
1033
+
1034
+ const hexOr = (value, fallback) => (/^#[0-9a-f]{6}$/i.test(value) ? value : fallback);
1035
+ const adjustables = [
1036
+ { key: "canvas", group: "Paper", label: "Chat paper", hint: "behind the messages", kind: "colour", of: (l) => l.palette.bg, vars: (v) => ({ "--grad-canvas": v, "--grad-page": v, "--canvas-pattern": "none" }) },
1037
+ { key: "panel", group: "Paper", label: "Panels", hint: "the rooms, the roster, the chat's frame, a vibemate's panel", kind: "colour", of: (l) => l.aliases.panel, vars: (v) => ({ "--panel": v }) },
1038
+ { key: "bubble", group: "Bubbles", label: "Reply bubble", kind: "colour", of: (l) => l.elements.bubble.bg, vars: (v) => ({ "--bubble-bg": v }) },
1039
+ { key: "mine", group: "Bubbles", label: "Your bubble", kind: "colour", of: (l) => l.palette.primary, vars: (v) => ({ "--bubble-mine-bg": `linear-gradient(${v}, ${v})` }) },
1040
+ { key: "ring", group: "Bubbles", label: "Bubble outline", hint: "a hairline around a reply; the bubble's own colour hides it", kind: "colour", of: (l) => hexOr(l.elements.bubble.border, l.elements.bubble.bg), vars: (v) => ({ "--bubble-border": v }) },
1041
+ { key: "ink", group: "Ink", label: "Words", kind: "colour", of: (l) => l.palette.ink, vars: (v) => ({ "--ink": v, "--ink-2": v, "--bubble-ink": v }) },
1042
+ { key: "muted", group: "Ink", label: "Quiet words", hint: "times, hints, labels", kind: "colour", of: (l) => l.palette.muted, vars: (v) => ({ "--muted": v, "--faint": v }) },
1043
+ { key: "accent", group: "Ink", label: "Accent", hint: "buttons, links, the ring on your face", kind: "colour", of: (l) => l.palette.primary, vars: (v) => ({ "--primary": v, "--grad-primary": `linear-gradient(135deg, ${v}, ${v})` }) },
1044
+ { key: "face", group: "Marks", label: "Face tile", hint: "the paper a face's colour is tinted into", kind: "colour", of: (l) => l.elements.face.paper, vars: (v) => ({ "--face-paper": v }) },
1045
+ { key: "logo", group: "Marks", label: "Logo tile", hint: "behind a vendor's mark", kind: "colour", of: (l) => l.elements.logoTile.bg, vars: (v) => ({ "--logo-tile-bg": v, "--logo-tile-badge-bg": v }) },
1046
+ { key: "logoInk", group: "Marks", label: "Logo ink", hint: "the mark itself", kind: "colour", of: (l) => l.elements.logoTile.ink, vars: (v) => ({ "--logo-tile-ink": v, "--logo-tile-badge-ink": v }) },
1047
+ { key: "corners", group: "Shape", label: "Corners", hint: "0 is square; every corner in the window scales with it", kind: "scale", min: 0, max: 1.5, step: 0.05, of: (l) => l.shape.rScale, vars: (v) => ({ "--r-scale": v }) },
1048
+ ];
1049
+
1050
+
1051
+ const SPEC_ID = /^[a-z][a-z0-9-]{0,30}$/;
1052
+ const HEX6 = /^#[0-9a-f]{6}$/i;
1053
+ const COLOUR_VALUE = /^(#[0-9a-f]{6}|#[0-9a-f]{8}|rgba?\([\d\s.,%]+\)|transparent|currentColor)$/i;
1054
+ const UNSAFE = /url\(|expression\(|javascript:|[<>{};\\@]/i;
1055
+ const FONT_STACK = /^[\w\s"',.-]+$/;
1056
+ const isColourDefault = (v) => COLOUR_VALUE.test(String(v).trim());
1057
+
1058
+ const hueOf = (token, palette, where) => {
1059
+ const t = String(token).trim();
1060
+ if (t.startsWith("$")) {
1061
+ const name = t.slice(1);
1062
+ if (!(name in palette)) throw new Error(`${where}: no hue "${name}" in the palette`);
1063
+ return palette[name];
1064
+ }
1065
+ if (HEX6.test(t)) return t.toLowerCase();
1066
+ throw new Error(`${where}: "${t}" is neither a hue of the palette ($name) nor a colour (#rrggbb)`);
1067
+ };
1068
+ const numberIn = (token, where, min, max) => {
1069
+ const n = Number(String(token).trim());
1070
+ if (!Number.isFinite(n) || n < min || n > max) throw new Error(`${where}: "${token}" is not a number between ${min} and ${max}`);
1071
+ return n;
1072
+ };
1073
+ const resolveValue = (raw, palette, where) => {
1074
+ if (typeof raw !== "string") throw new Error(`${where}: a value is a string`);
1075
+ const v = raw.trim();
1076
+ if (!v) throw new Error(`${where}: an empty value`);
1077
+ if (v.length > 400) throw new Error(`${where}: a value is at most 400 characters`);
1078
+ if (UNSAFE.test(v)) throw new Error(`${where}: a value may not carry url(), expression(), javascript: or < > { } ; \\ @`);
1079
+ const out = v
1080
+ .replace(/\b(alpha|mix)\(([^()]*)\)/g, (m, fn, args) => {
1081
+ const parts = args.split(",").map((s) => s.trim());
1082
+ if (fn === "alpha") {
1083
+ if (parts.length !== 2) throw new Error(`${where}: alpha takes a hue and a number: alpha($ink, 0.2)`);
1084
+ return alpha(hueOf(parts[0], palette, where), numberIn(parts[1], where, 0, 1));
1085
+ }
1086
+ if (parts.length !== 3) throw new Error(`${where}: mix takes two hues and a number: mix($white, $ink, 0.5)`);
1087
+ return mix(hueOf(parts[0], palette, where), hueOf(parts[1], palette, where), numberIn(parts[2], where, 0, 1));
1088
+ })
1089
+ .replace(/\$([a-zA-Z][a-zA-Z0-9]*)/g, (m, name) => hueOf(`$${name}`, palette, where));
1090
+ if (/\$/.test(out)) throw new Error(`${where}: a stray $ (a hue is $name)`);
1091
+ return out;
1092
+ };
1093
+ const checked = (defaultValue, value, where) => {
1094
+ if (isColourDefault(defaultValue) && !COLOUR_VALUE.test(value)) throw new Error(`${where}: a colour (the look has ${defaultValue}), not "${value}"`);
1095
+ return value;
1096
+ };
1097
+ const flatSection = (spec, section, defaults, baseWants, palette) => {
1098
+ const out = { ...(baseWants || {}) };
1099
+ for (const [key, raw] of Object.entries(spec || {})) {
1100
+ if (!(key in defaults)) throw new Error(`${section}.${key}: no such token (${Object.keys(defaults).join(", ")})`);
1101
+ out[key] = checked(defaults[key], resolveValue(raw, palette, `${section}.${key}`), `${section}.${key}`);
1102
+ }
1103
+ return out;
1104
+ };
1105
+
1106
+ function make(spec) {
1107
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) throw new Error("a look spec is an object");
1108
+ const id = String(spec.id ?? "").trim();
1109
+ if (!SPEC_ID.test(id)) throw new Error("id: a short lower-case id of letters, digits and hyphens (1-31), starting with a letter");
1110
+ if (looks[id] && !looks[id].custom) throw new Error(`id "${id}" belongs to a look viberoom ships; pick another`);
1111
+ const label = String(spec.label ?? "").trim();
1112
+ if (!label || label.length > 40) throw new Error("label: 1-40 characters, the name the picker shows");
1113
+ const baseId = spec.extends === undefined || spec.extends === null || spec.extends === "" ? "classic" : String(spec.extends);
1114
+ const base = looks[baseId];
1115
+ if (!base) throw new Error(`extends: no look "${baseId}" (${Object.keys(looks).filter((k) => !looks[k].custom).join(", ")})`);
1116
+ if (base.custom) throw new Error(`extends: "${baseId}" is a look of the human's own; a look extends one viberoom ships`);
1117
+ const scheme = spec.scheme === undefined || spec.scheme === null || spec.scheme === "" ? base.scheme : String(spec.scheme);
1118
+ if (scheme !== "light" && scheme !== "dark") throw new Error('scheme: "light" or "dark"');
1119
+ for (const key of Object.keys(spec)) {
1120
+ if (!["id", "label", "scheme", "extends", "author", "created", "updatedAt", "palette", "shape", "type", "motion", "elevation", "canvas", "elements"].includes(key)) throw new Error(`${key}: not a part of a look spec`);
1121
+ }
1122
+ const section = (name) => {
1123
+ const v = spec[name];
1124
+ if (v === undefined || v === null) return {};
1125
+ if (typeof v !== "object" || Array.isArray(v)) throw new Error(`${name}: an object of keys and values`);
1126
+ return v;
1127
+ };
1128
+ const palette = { ...base.palette };
1129
+ for (const [name, raw] of Object.entries(section("palette"))) {
1130
+ if (!(name in palette)) throw new Error(`palette.${name}: no such hue (${Object.keys(palette).join(", ")})`);
1131
+ const v = resolveValue(raw, palette, `palette.${name}`);
1132
+ if (!HEX6.test(v)) throw new Error(`palette.${name}: a hue is a flat colour #rrggbb, not "${v}"`);
1133
+ palette[name] = v.toLowerCase();
1134
+ }
1135
+ const baseWants = base.wants || {};
1136
+ const typeWants = { ...(baseWants.type || {}) };
1137
+ for (const [key, raw] of Object.entries(section("type"))) {
1138
+ if (!(key in type)) throw new Error(`type.${key}: no such token (${Object.keys(type).join(", ")})`);
1139
+ const v = resolveValue(raw, palette, `type.${key}`);
1140
+ if (key === "font" || key === "mono") {
1141
+ const table = key === "font" ? fonts.text : fonts.mono;
1142
+ if (table[v]) typeWants[key] = table[v].stack;
1143
+ else if (FONT_STACK.test(v)) typeWants[key] = v;
1144
+ else throw new Error(`type.${key}: a font id (${Object.keys(table).join(", ")}) or a family stack`);
1145
+ } else typeWants[key] = v;
1146
+ }
1147
+ const specCanvas = section("canvas");
1148
+ const specElements = section("elements");
1149
+ const wants = {
1150
+ shape: flatSection(section("shape"), "shape", shape, baseWants.shape, palette),
1151
+ type: typeWants,
1152
+ motion: flatSection(section("motion"), "motion", motion, baseWants.motion, palette),
1153
+ elevation: flatSection(section("elevation"), "elevation", elevationOf(palette), baseWants.elevation, palette),
1154
+ canvas: (p) => flatSection(specCanvas, "canvas", canvasOf(p), baseWants.canvas ? baseWants.canvas(p) : {}, p),
1155
+ elements: (p) => {
1156
+ const defaults = elementsOf(p);
1157
+ const out = {};
1158
+ const fromBase = baseWants.elements ? baseWants.elements(p) : {};
1159
+ for (const [group, parts] of Object.entries(fromBase)) out[group] = { ...parts };
1160
+ for (const [group, parts] of Object.entries(specElements)) {
1161
+ if (!(group in defaults)) throw new Error(`elements.${group}: no such element (${Object.keys(defaults).join(", ")})`);
1162
+ if (!parts || typeof parts !== "object" || Array.isArray(parts)) throw new Error(`elements.${group}: an object of keys and values`);
1163
+ out[group] = { ...(out[group] || {}) };
1164
+ for (const [key, raw] of Object.entries(parts)) {
1165
+ if (!(key in defaults[group])) throw new Error(`elements.${group}.${key}: no such token (${Object.keys(defaults[group]).join(", ")})`);
1166
+ out[group][key] = checked(defaults[group][key], resolveValue(raw, p, `elements.${group}.${key}`), `elements.${group}.${key}`);
1167
+ }
1168
+ }
1169
+ return out;
1170
+ },
1171
+ };
1172
+ const made = look(id, label, scheme, palette, wants);
1173
+ made.custom = true;
1174
+ made.extends = base.id;
1175
+ made.author = spec.author === undefined || spec.author === null ? "" : String(spec.author).slice(0, 40);
1176
+ return made;
1177
+ }
1178
+
1179
+ const docs = {
1180
+ sections: {
1181
+ palette: "the hues of the look, named after what they look like, never after what they are for; every element is derived from them, so a hue changed here reaches every place that wears it",
1182
+ shape: "corners: rScale multiplies every radius in the window (0 square, 1 as designed, 1.6 very round); rCtlMin is the least radius of a button, an icon button or a chip (0px keeps each one's own, 99px makes every control a pill); rXs…rPill are the named radii, each ONE length or calc() (they are used inside calc() and max(), so a multi-corner value breaks every corner)",
1183
+ type: "text: font and mono are the family stacks (in a spec: an id from fonts, or a stack), lineHeight the running text's, fsScale multiplies every text size, fsXs…fs2xl are the named sizes",
1184
+ motion: "the durations (tFast, tBase, tSlow; 0ms for none) and easings of the transitions",
1185
+ elevation: "the shadows and the light: edge (a hair under a panel), shadowTile (a raised tile), shadowPop (a floating balloon), shadowPrimary (the glow under the accent), shadowDialog, shadowCard, shadowCtl and shadowCtlHover (a control at rest and under the pointer), shadowInset (a sunken thing), shadowSoft (a small raised thing), shadow / shadow1 / shadow2 (older names), bevel (a light laid over a raised surface: a gradient, or none), bevelPressed (the same when pressed), shadowRoom (room around a message for its shadow, 0px unless the shadows reach out)",
1186
+ canvas: "the chat's paper: gradCanvas (the paint behind the messages: a colour, or gradients), canvasPattern and canvasPatternSize (a pattern over it, or none / 0 0), gradPage (the paper behind the panels), sbW / sbThumb / sbThumbHover (the scrollbar)",
1187
+ elements: "every UI element and what it wears: the group is the element, the key the part (bg, ink, hoverBg, border, shadow, radius…); a key ending in Ink is written on the key of the same stem ending in Bg (or on bg), and the pair must read at 3:1 or better; a key whose value is none / 0px / transparent is structure a look may switch on (a border, a shadow, a lift, capitals)",
1188
+ },
1189
+ palette: {
1190
+ primary: "the one accent: buttons, links, your bubble, the ring on your face",
1191
+ primaryLight: "the accent a step lighter (the start of its gradient)",
1192
+ primaryDeep: "the accent a step deeper (the end of its gradient)",
1193
+ primaryDark: "the accent's darkest step: the accent as an ink on its own tint",
1194
+ violet: "the far end of the welcome hero's sweep",
1195
+ iris: "the colour the browser paints its own window frame with",
1196
+ ink: "the words",
1197
+ ink2: "the words in a bubble",
1198
+ ink3: "quieter words: a button's, labels",
1199
+ indigo: "the human's initials on their face; a vendor's mark",
1200
+ muted: "quiet words: times, hints",
1201
+ faint: "the faintest words and dots",
1202
+ placeholder: "a field's placeholder",
1203
+ grey: "a face with no colour of its own",
1204
+ white: "the paper of panels, cards and bubbles (on a dark look: the dark paper)",
1205
+ black: "black, for shadows cast in black",
1206
+ bg: "the paper behind everything",
1207
+ soft: "the paper a step off the panel's: fields, tiles",
1208
+ softer: "the paper two steps off: buttons at rest",
1209
+ lav: "the accent's tint: tiles, chips, hover",
1210
+ lav2: "the accent's deeper tint: borders, a pressed chip",
1211
+ canvasTop: "the chat paper's sweep, top",
1212
+ canvasMid: "the chat paper's sweep, middle",
1213
+ canvasBottom: "the chat paper's sweep, bottom",
1214
+ shadowInk: "the colour shadows are cast in",
1215
+ mint: "green, pale: done, ready",
1216
+ mint2: "green, pale, a step deeper: hover on a green thing",
1217
+ mintTint: "green, the faintest tint",
1218
+ mintInk: "green as an ink on the pale green",
1219
+ forest: "green, dark: words on the hero's green bubble",
1220
+ green: "green, bright: the ready dot",
1221
+ warm: "yellow, pale: thinking, attention, a note",
1222
+ warm2: "yellow, pale, a step deeper: hover on a note",
1223
+ warmInk: "yellow as an ink on the pale yellow",
1224
+ bronze: "yellow, dark: words on the hero's yellow bubble",
1225
+ amber: "amber, bright: the thinking dot, a warning",
1226
+ honey: "honey: the number and the found line in code",
1227
+ spark: "the spark on the hero",
1228
+ peach: "peach, pale: a welcome tile",
1229
+ peach2: "peach, pale, a step deeper",
1230
+ orange: "orange: attention",
1231
+ orangeLight: "orange, light: the attention gradient's start",
1232
+ orangeDeep: "orange, deep: the attention gradient's end",
1233
+ orangeDark: "orange, dark: the attention ink",
1234
+ ember: "ember: unread, a glow",
1235
+ emberDeep: "ember, deep: the unread gradient's end",
1236
+ rose: "red, pale: an error, a danger button",
1237
+ rose2: "red, pale, a step deeper: hover on a danger button",
1238
+ roseInk: "red as an ink: errors, the danger ink, a failed call",
1239
+ roseDark: "red, dark: a solid danger button pressed",
1240
+ pink: "pink: the unseen line",
1241
+ pinkTint: "pink, the faintest tint: the unseen line's label",
1242
+ sky: "blue, pale: waiting, info",
1243
+ blue: "blue: info",
1244
+ blueDeep: "blue, deep: the waiting ink",
1245
+ blueSoft: "blue, soft: the waiting dot",
1246
+ orchid: "violet, pale: writing",
1247
+ orchidInk: "violet as an ink: the writing ink",
1248
+ orchidMid: "violet, mid: the writing dot",
1249
+ periwinkle: "periwinkle: your ticks on the timeline",
1250
+ periwinkleDeep: "periwinkle, deep: your ticks in view",
1251
+ deep: "the dark surface of code blocks and the file viewer",
1252
+ deepGutter: "the gutter of the dark surface",
1253
+ deepInk: "the words on the dark surface",
1254
+ deepMuted: "the quiet words on the dark surface",
1255
+ deepHead: "the head of the dark surface",
1256
+ deepMeta: "the meta words in that head",
1257
+ lavGrey: "code: punctuation",
1258
+ lilac: "code: keywords",
1259
+ seafoam: "code: strings",
1260
+ azure: "code: functions",
1261
+ salmon: "code: tags",
1262
+ magenta: "code: attributes",
1263
+ },
1264
+ elements: {
1265
+ page: "the page itself: its paper, its ink, the selection",
1266
+ focus: "the ring around whatever the keyboard is on",
1267
+ link: "a path or a link inside a message",
1268
+ btn: "button: a button with words; the colours of every kind (primary, secondary, soft, ghost, danger, danger-solid, dark, warn, inverse, link, ok, paper), its sizes, and its structure (border: width style colour; case; tracking; ghostBg; the shadows at rest / on hover / pressed; hoverLift: negative lifts)",
1269
+ iconButton: "icon-button: a button that is only a glyph; kinds primary, danger, ghost, inline; its sizes and structure (ghostBg, shadows, hoverLift)",
1270
+ badge: "badge: a small label that states a fact; the state tones come from the room's meaning colours; fill (100% filled, 0% an outline in its ink), border, case, tracking, shadow",
1271
+ chip: "chip: a small tag that names a thing; fill, border, shadow, hoverLift, activeDrop",
1272
+ fileCard: "file-card: a fragment of a file or a picture under the message that names it (the dark card)",
1273
+ toolCall: "tool-call: a chip that opens to the call; the status colours",
1274
+ toolFold: "tool-fold: the calls of a finished reply, in one line",
1275
+ askCard: "ask-card: a decision the room puts in front of you (a permission, a proposal)",
1276
+ replyNote: "reply-note: a line the hub attaches to a reply, inside its bubble",
1277
+ lookCard: "look-card: a look as a small picture of itself (the picker)",
1278
+ choice: "choice: one option of a few, as a pill; border, shadows, hoverLift, activeDrop",
1279
+ control: "switches and checkboxes",
1280
+ tile: "the small marks that are not types yet: the ? badge of the life ring, the count pills",
1281
+ roomMark: "room-mark: a room's square mark in its own hue (ink, the recipe of the gradient, shadow, radius / radiusLg / radiusRail)",
1282
+ pop: "the little speech balloons the rail and the participants raise",
1283
+ dialog: "the glass behind a dialog",
1284
+ scrim: "the dark glass behind a picture or a diagram",
1285
+ toast: "a toast: the ink with the paper's colour on it",
1286
+ rail: "the main menu",
1287
+ timeline: "the strips beside the chat",
1288
+ bubble: "the message balloon: everyone else's (bg, ink, border, shadow) and your own (mineBg: always an image, a flat colour as linear-gradient(c, c); mineInk, mineShadow and the parts inside it)",
1289
+ quote: "a quoted message inside a balloon",
1290
+ attach: "pictures and file chips in a message",
1291
+ table: "a table in a message",
1292
+ code: "the file viewer and every code block",
1293
+ syntax: "the colours of the code itself",
1294
+ diagram: "a mermaid diagram opened full screen",
1295
+ input: "text fields (bg, focusBorder, shadow: none, or a sunken recipe)",
1296
+ profile: "the human's own card",
1297
+ face: "face: a participant's face (paper and tint of the tile, corner, rings, shadow, cardShadow)",
1298
+ logoTile: "logo-tile: a vendor's mark on a tile (bg, ink, the badge in a face's corner, shadow, radius / radiusSm / radiusLg / radiusBadge)",
1299
+ unseenLine: "unseen-line: the line above which a vibemate has seen nothing (ink, bg, the line's drawing, the label's border / case / tracking / shadow)",
1300
+ numberField: "number-field: a number with its own steps",
1301
+ adjustRow: "adjust-row: one thing the human may adjust in a look, in Settings",
1302
+ rowButton: "row-button: a small action on a vibemate's row",
1303
+ hubRow: "hub-row: a line the hub writes (ink, tones, the one that leads somewhere: actionBg / actionShadow; prefix / prefixGap: what stands before the line, none or a prompt)",
1304
+ hero: "the welcome screen's hero",
1305
+ feature: "the welcome screen's feature tiles (emojiBg, shadow)",
1306
+ composer: "the box you write in (shadow, bg, radius, padX: flat and bare, or sunk into the paper)",
1307
+ step: "the welcome screen's numbered steps",
1308
+ tpl: "the template list in Open a room",
1309
+ browser: "the colour the browser paints its window frame with",
1310
+ },
1311
+ };
1312
+
1313
+ const KIND_SYNTAX = {
1314
+ border: 'a CSS border: width, style and colour, e.g. "2px solid $ink" (a width alone draws nothing); 0 for none',
1315
+ shadow: 'a CSS box-shadow list, e.g. "3px 4px 0 $ink" or "0 2px 6px alpha($ink, 0.2)", or none',
1316
+ radius: "ONE length or calc(), e.g. \"calc(14px * var(--r-scale))\": the named radii are used inside calc() and max(), so a multi-corner value (four lengths, or a slash) breaks every corner",
1317
+ lift: 'a length the thing moves on hover: NEGATIVE lifts it ("-2px"), positive pushes it down; 0px for none',
1318
+ drop: 'a length the thing sinks when pressed ("1px"); 0px for none',
1319
+ fill: 'a percentage of the tone\'s colour: "100%" filled, "0%" an outline in the ink',
1320
+ prefix: 'a quoted string written before the line, e.g. "\">\"", or none',
1321
+ case: "none, uppercase, lowercase or capitalize",
1322
+ tracking: 'a letter-spacing length, e.g. "0.06em" or 0px',
1323
+ scale: 'a number as a string ("1", "0.5", "1.6")',
1324
+ length: 'a CSS length, e.g. "12px"',
1325
+ gradient: "a CSS gradient image (linear-gradient / radial-gradient), a flat colour, or none",
1326
+ bevel: "a gradient image laid over a raised face, or none",
1327
+ font: "a font id from fonts (text or mono), or a family stack",
1328
+ };
1329
+ const kindOf = (section, key, value) => {
1330
+ const v = String(value);
1331
+ if (COLOUR_VALUE.test(v)) return "colour";
1332
+ if (section === "shape") return key === "rScale" ? "scale" : "radius";
1333
+ if (section === "type") return key === "font" || key === "mono" ? "font" : /^fs/.test(key) ? "length" : "scale";
1334
+ if (section === "elevation") return /^bevel/.test(key) ? "bevel" : key === "shadowRoom" ? "length" : "shadow";
1335
+ if (section === "canvas") return /^grad|Pattern$/.test(key) ? "gradient" : "length";
1336
+ if (/[Rr]adius/.test(key)) return "radius";
1337
+ if (/[Ss]hadow$/.test(key)) return "shadow";
1338
+ if (key === "border" || /Border$/.test(key)) return "border";
1339
+ if (/Lift$/.test(key)) return "lift";
1340
+ if (/Drop$/.test(key)) return "drop";
1341
+ if (key === "fill") return "fill";
1342
+ if (key === "prefix") return "prefix";
1343
+ if (key === "case" || /Case$/.test(key)) return "case";
1344
+ if (/[Tt]racking$/.test(key)) return "tracking";
1345
+ if (/Bg$|^bg$|Ink$|^ink$|Color$/.test(key) || /gradient\(/.test(v)) return "gradient";
1346
+ return null;
1347
+ };
1348
+
1349
+ function describe() {
1350
+ const l = looks.classic;
1351
+ const keysOf = (obj) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, String(v)]));
1352
+ const kinds = {};
1353
+ const structural = (kind) => kind && kind !== "colour" && kind !== "gradient";
1354
+ for (const section of ["shape", "type", "elevation", "canvas"]) for (const [k, v] of Object.entries(l[section])) { const kind = kindOf(section, k, v); if (structural(kind)) kinds[`${section}.${k}`] = kind; }
1355
+ for (const [g, parts] of Object.entries(l.elements)) for (const [k, v] of Object.entries(parts)) { const kind = kindOf("elements", k, v); if (structural(kind)) kinds[`elements.${g}.${k}`] = kind; }
1356
+ return {
1357
+ syntax: "A look spec is { id, label, scheme?, extends?, palette?, shape?, type?, motion?, elevation?, canvas?, elements? }. It extends one of the looks viberoom ships (VibeClassic unless said) and changes only the keys it gives. A value is a string: a colour \"#1a2b3c\"; \"$name\" for a hue of the palette; \"alpha($ink, 0.14)\" for a hue at an alpha; \"mix($white, $ink, 0.5)\" for a blend; anything else (a shadow, a size, a gradient, a font) as CSS, with those forms inside it. A key the look does not have is refused; a colour key takes only a colour; no url(), no braces.",
1358
+ sections: docs.sections,
1359
+ palette: Object.fromEntries(Object.keys(l.palette).map((n) => [n, { doc: docs.palette[n] || "", classic: l.palette[n] }])),
1360
+ shape: keysOf(l.shape),
1361
+ type: keysOf(l.type),
1362
+ motion: keysOf(l.motion),
1363
+ elevation: keysOf(l.elevation),
1364
+ canvas: keysOf(l.canvas),
1365
+ elements: Object.fromEntries(Object.entries(l.elements).map(([g, parts]) => [g, { doc: docs.elements[g] || "", keys: keysOf(parts) }])),
1366
+ kinds,
1367
+ kindSyntax: KIND_SYNTAX,
1368
+ fonts: { text: Object.keys(fonts.text), mono: Object.keys(fonts.mono) },
1369
+ looks: Object.values(looks).map((x) => ({ id: x.id, label: x.label, scheme: x.scheme, custom: !!x.custom, extends: x.extends || null })),
1370
+ adjustables: adjustables.map((a) => ({ key: a.key, group: a.group, label: a.label, hint: a.hint || "", kind: a.kind, min: a.min, max: a.max, step: a.step })),
1371
+ };
1372
+ }
1373
+
1374
+ globalThis.VIBEROOM_TOKENS = { looks, current: looks.classic, active, diagrams, adjustables, cssGroups, cssVars, alpha, mix, kebab, fonts, make, describe, docs, kindOf };
620
1375
  })();