@catandbox/schrodinger-web-adapter 0.1.26 → 0.1.28

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.
@@ -1,5 +1,169 @@
1
1
  import { renderStatusBadge } from "./status-badge";
2
2
  import { setInnerHtml } from "./dom-utils";
3
+ function loadThree() {
4
+ return new Promise((resolve, reject) => {
5
+ if (typeof THREE !== "undefined") {
6
+ resolve();
7
+ return;
8
+ }
9
+ const s = document.createElement("script");
10
+ s.src = "https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js";
11
+ s.onload = () => resolve();
12
+ s.onerror = reject;
13
+ document.head.appendChild(s);
14
+ });
15
+ }
16
+ function initHydrogenLogo(el) {
17
+ // Math helpers
18
+ function factorial(n) {
19
+ let r = 1;
20
+ for (let i = 2; i <= n; i++)
21
+ r *= i;
22
+ return r;
23
+ }
24
+ function assocLaguerre(p, q, x) {
25
+ if (p === 0)
26
+ return 1;
27
+ if (p === 1)
28
+ return 1 + q - x;
29
+ let lm2 = 1, lm1 = 1 + q - x, val = 0;
30
+ for (let k = 2; k <= p; k++) {
31
+ val = ((2 * k - 1 + q - x) * lm1 - (k - 1 + q) * lm2) / k;
32
+ lm2 = lm1;
33
+ lm1 = val;
34
+ }
35
+ return val;
36
+ }
37
+ function assocLegendre(l, m, x) {
38
+ let pmm = 1.0;
39
+ if (m > 0) {
40
+ const s = Math.sqrt((1 - x) * (1 + x));
41
+ let f = 1;
42
+ for (let i = 1; i <= m; i++) {
43
+ pmm *= -f * s;
44
+ f += 2;
45
+ }
46
+ }
47
+ if (l === m)
48
+ return pmm;
49
+ let pmmp1 = x * (2 * m + 1) * pmm;
50
+ if (l === m + 1)
51
+ return pmmp1;
52
+ let pll = 0;
53
+ for (let ll = m + 2; ll <= l; ll++) {
54
+ pll = (x * (2 * ll - 1) * pmmp1 - (ll + m - 1) * pmm) / (ll - m);
55
+ pmm = pmmp1;
56
+ pmmp1 = pll;
57
+ }
58
+ return pll;
59
+ }
60
+ function sphericalHarmonicReal(l, m, theta, phi) {
61
+ const absM = Math.abs(m);
62
+ const norm = Math.sqrt(((2 * l + 1) / (4 * Math.PI)) * (factorial(l - absM) / factorial(l + absM)));
63
+ const plm = assocLegendre(l, absM, Math.cos(theta));
64
+ if (m > 0)
65
+ return norm * plm * Math.cos(m * phi) * Math.SQRT2;
66
+ if (m < 0)
67
+ return norm * plm * Math.sin(absM * phi) * Math.SQRT2;
68
+ return norm * plm;
69
+ }
70
+ function radialWaveFunction(n, l, r) {
71
+ const rho = (2 * r) / n;
72
+ const norm = Math.sqrt(Math.pow(2 / n, 3) * factorial(n - l - 1) / (2 * n * Math.pow(factorial(n + l), 3)));
73
+ return norm * Math.exp(-rho / 2) * Math.pow(rho, l) * assocLaguerre(n - l - 1, 2 * l + 1, rho);
74
+ }
75
+ function psiProb(n, l, m, r, theta, phi) {
76
+ const R = radialWaveFunction(n, l, r);
77
+ const Y = sphericalHarmonicReal(l, m, theta, phi);
78
+ return R * R * Y * Y;
79
+ }
80
+ // Generate points via rejection sampling
81
+ const n = 4, l = 3, m = 1, maxR = 45, numPoints = 80000;
82
+ let maxProb = 0;
83
+ for (let i = 0; i < 5000; i++) {
84
+ const r = Math.random() * maxR;
85
+ const theta = Math.acos(2 * Math.random() - 1);
86
+ const phi = Math.random() * 2 * Math.PI;
87
+ const p = psiProb(n, l, m, r, theta, phi) * r * r;
88
+ if (p > maxProb)
89
+ maxProb = p;
90
+ }
91
+ const pts = [], cols = [], szs = [];
92
+ let accepted = 0;
93
+ const sc = 0.7;
94
+ for (let i = 0; i < numPoints * 30 && accepted < numPoints; i++) {
95
+ const r = Math.random() * maxR;
96
+ const theta = Math.acos(2 * Math.random() - 1);
97
+ const phi = Math.random() * 2 * Math.PI;
98
+ const prob = psiProb(n, l, m, r, theta, phi) * r * r;
99
+ const thr = prob / maxProb;
100
+ if (Math.random() < thr) {
101
+ pts.push(r * Math.sin(theta) * Math.cos(phi) * sc, r * Math.sin(theta) * Math.sin(phi) * sc, r * Math.cos(theta) * sc);
102
+ const t = r / maxR;
103
+ cols.push(0.3 + 0.6 * t, 0.5 * (1 - t) + 0.2 * t, 0.95 - 0.2 * t);
104
+ szs.push(0.08 + 0.15 * thr);
105
+ accepted++;
106
+ }
107
+ }
108
+ const scene = new THREE.Scene();
109
+ const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);
110
+ const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
111
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
112
+ renderer.setSize(80, 80);
113
+ renderer.setClearColor(0x08090d, 1);
114
+ el.appendChild(renderer.domElement);
115
+ const geometry = new THREE.BufferGeometry();
116
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(pts, 3));
117
+ geometry.setAttribute("color", new THREE.Float32BufferAttribute(cols, 3));
118
+ geometry.setAttribute("size", new THREE.Float32BufferAttribute(szs, 1));
119
+ const material = new THREE.ShaderMaterial({
120
+ vertexShader: `
121
+ attribute float size;
122
+ varying vec3 vColor;
123
+ void main() {
124
+ vColor = color;
125
+ vec4 mv = modelViewMatrix * vec4(position, 1.0);
126
+ gl_PointSize = size * (200.0 / -mv.z);
127
+ gl_Position = projectionMatrix * mv;
128
+ }
129
+ `,
130
+ fragmentShader: `
131
+ varying vec3 vColor;
132
+ void main() {
133
+ float d = length(gl_PointCoord - vec2(0.5));
134
+ if (d > 0.5) discard;
135
+ float a = smoothstep(0.5, 0.1, d) * 0.7;
136
+ gl_FragColor = vec4(vColor, a);
137
+ }
138
+ `,
139
+ vertexColors: true,
140
+ transparent: true,
141
+ depthWrite: false,
142
+ blending: THREE.AdditiveBlending,
143
+ });
144
+ scene.add(new THREE.Points(geometry, material));
145
+ const nucGeo = new THREE.SphereGeometry(0.25, 12, 12);
146
+ scene.add(new THREE.Mesh(nucGeo, new THREE.MeshBasicMaterial({ color: 0xffffff })));
147
+ let theta = 0;
148
+ const phi = 0.85, radius = 42;
149
+ function updateCam() {
150
+ camera.position.set(radius * Math.sin(phi) * Math.sin(theta), radius * Math.cos(phi), radius * Math.sin(phi) * Math.cos(theta));
151
+ camera.lookAt(0, 0, 0);
152
+ }
153
+ let rafId;
154
+ function animate() {
155
+ rafId = requestAnimationFrame(animate);
156
+ theta += 0.003;
157
+ updateCam();
158
+ renderer.render(scene, camera);
159
+ }
160
+ updateCam();
161
+ animate();
162
+ return () => {
163
+ cancelAnimationFrame(rafId);
164
+ renderer.dispose();
165
+ };
166
+ }
3
167
  function formatDate(timestamp) {
4
168
  return new Date(timestamp * 1000).toLocaleDateString(undefined, {
5
169
  month: "short",
@@ -35,10 +199,11 @@ const ORDER_OPTIONS = [
35
199
  { value: "oldest", label: "Oldest first" }
36
200
  ];
37
201
  export async function renderTicketList(container, client, emitter) {
38
- // Load tickets and categories in parallel
202
+ // Load tickets, categories and Three.js in parallel
39
203
  const [result, portalConfig] = await Promise.all([
40
204
  client.listTickets({}).catch(() => ({ items: [] })),
41
- client.getPortalConfig().catch(() => ({ categories: [], aliases: [] }))
205
+ client.getPortalConfig().catch(() => ({ categories: [], aliases: [] })),
206
+ loadThree().catch(() => { })
42
207
  ]);
43
208
  const allTickets = result.items;
44
209
  const categories = portalConfig.categories;
@@ -57,10 +222,10 @@ export async function renderTicketList(container, client, emitter) {
57
222
  <s-box padding="large">
58
223
  <s-stack gap="large">
59
224
  <div style="display:flex; justify-content:space-between; align-items:center;">
60
- <s-stack gap="extraTight">
61
- <s-text variant="headingMd">Support Tickets</s-text>
62
- <s-text variant="bodySm" tone="subdued">View and manage your support requests</s-text>
63
- </s-stack>
225
+ <div style="display:flex; align-items:center; gap:12px;">
226
+ <div id="sch-logo-el" style="width:80px; height:80px; border-radius:8px; overflow:hidden; flex-shrink:0; background:#08090d;"></div>
227
+ <span style="font-size:18px; font-weight:700; color:#0f172a;">Schrödinger helpdesk by Cat and box</span>
228
+ </div>
64
229
  <s-button variant="primary" id="sch-new-ticket-btn">
65
230
  <span style="display:inline-flex; align-items:center; gap:4px;">
66
231
  <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor"><path d="M10 3a1 1 0 0 1 1 1v5h5a1 1 0 1 1 0 2h-5v5a1 1 0 1 1-2 0v-5H4a1 1 0 1 1 0-2h5V4a1 1 0 0 1 1-1z"/></svg>
@@ -96,6 +261,11 @@ export async function renderTicketList(container, client, emitter) {
96
261
  container
97
262
  .querySelector("#sch-new-ticket-btn")
98
263
  ?.addEventListener("click", () => emitter.emit("ticket:create", undefined));
264
+ // Init hydrogen orbital logo
265
+ const logoEl = container.querySelector("#sch-logo-el");
266
+ if (logoEl && typeof THREE !== "undefined") {
267
+ initHydrogenLogo(logoEl);
268
+ }
99
269
  function getSelectValue(id) {
100
270
  const el = container.querySelector(`#${id}`);
101
271
  return el?.value ?? "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catandbox/schrodinger-web-adapter",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",