@ohif/app 3.13.0-beta.116 → 3.13.0-beta.117

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 (47) hide show
  1. package/dist/{1206.bundle.47f070e2254d128c.js → 1206.bundle.e04ca1efe6f987ba.js} +1 -1
  2. package/dist/1483.bundle.ab1e3a882b58eb79.js +470 -0
  3. package/dist/{1602.bundle.097ec19e8e61516f.js → 1602.bundle.ea4c1b43123dd2b3.js} +2 -2
  4. package/dist/{7701.bundle.945445d6681ddd20.js → 2343.bundle.48839e1b3fa4505e.js} +990 -7
  5. package/dist/{3500.bundle.1e063b7b4910d6cf.js → 3500.bundle.da69a02594beccc8.js} +1 -1
  6. package/dist/{3760.bundle.6c7eeb4b7f1583ca.js → 3760.bundle.e406b1c3fadf2a3b.js} +2 -2
  7. package/dist/{5448.bundle.1e50777e4c4e122a.js → 5448.bundle.5686ebb99c379f64.js} +1 -1
  8. package/dist/{5696.bundle.9f0366ad867385bc.js → 5696.bundle.4e44ce1f590134eb.js} +7 -4
  9. package/dist/{5910.bundle.5561806884c95754.js → 5910.bundle.0f19608f3e76859b.js} +2 -2
  10. package/dist/{419.bundle.9d0e6a72e1e3a17b.js → 6749.bundle.8c3dd9258f09df24.js} +288 -62
  11. package/dist/{7248.bundle.f761b21f343d4b9f.js → 7248.bundle.11ec7ee7e10b0a34.js} +2 -2
  12. package/dist/{7295.bundle.4d4b6de4d2bb8856.js → 7499.bundle.0f065214db09a60b.js} +502 -5
  13. package/dist/{781.bundle.efb0a6981676cebb.js → 781.bundle.3c1a02ca26d4565a.js} +2 -2
  14. package/dist/{8423.bundle.5d1b3092bc9562c9.js → 8423.bundle.f8e7d0f1eba7e178.js} +2 -2
  15. package/dist/8492.bundle.de58afbc98ce7972.js +504 -0
  16. package/dist/8595.css +2 -0
  17. package/dist/{899.bundle.e786f8dfb66d74ca.js → 899.bundle.50c16f860acbc848.js} +2 -2
  18. package/dist/{9068.bundle.cd36fcb4e15a153e.js → 9068.bundle.95f1e638ef694ea9.js} +2 -2
  19. package/dist/{9402.bundle.6549cc304496cd5c.js → 9402.bundle.6a8a324e963b4099.js} +2 -2
  20. package/dist/{9415.bundle.2a970c71de4014bf.js → 9415.bundle.20fee612b3b3dbfd.js} +2 -2
  21. package/dist/{9437.bundle.0fff227a859992ea.js → 9437.bundle.f3f5aff637471ac4.js} +107 -123
  22. package/dist/app-config.js +1 -1
  23. package/dist/{app.bundle.55057e411d635dc3.js → app.bundle.a73ad7e598640adf.js} +203 -36
  24. package/dist/customizations/index.html +400 -0
  25. package/dist/customizations/segmentation/segmentationAnnotationTools.jsonc +48 -0
  26. package/dist/customizations/segmentation/segmentationEditing.jsonc +59 -0
  27. package/dist/customizations/{ctPresets.jsonc → tools/ctPresets.jsonc} +1 -1
  28. package/dist/customizations/{measurementLabels.jsonc → tools/measurementLabels.jsonc} +1 -1
  29. package/dist/customizations/{smoothRotate.jsonc → tools/smoothRotate.jsonc} +1 -1
  30. package/dist/customizations/veterinary/veterinary.jsonc +5 -0
  31. package/dist/customizations/{veterinaryOverlay.jsonc → veterinary/veterinaryOverlay.jsonc} +3 -3
  32. package/dist/customizations/{patientBirthDate.jsonc → worklist/patientBirthDate.jsonc} +2 -2
  33. package/dist/index.html +1 -1
  34. package/dist/sw.js +1 -1
  35. package/package.json +5 -5
  36. package/dist/4834.bundle.a91450199d0f45e4.js +0 -900
  37. package/dist/6001.bundle.0789789db5410764.js +0 -1529
  38. package/dist/customizations/veterinary.jsonc +0 -5
  39. /package/dist/{100.bundle.ac04a425c0851574.js → 100.bundle.ec5ee3b5f479a217.js} +0 -0
  40. /package/dist/{1180.bundle.c19c443fe8d26719.js → 1180.bundle.9bed26c1a1e75389.js} +0 -0
  41. /package/dist/{2779.bundle.dca036a18399be96.js → 2779.bundle.2a225a69fb65c30d.js} +0 -0
  42. /package/dist/{4556.bundle.b705ab15473128c7.js → 4556.bundle.735ccc9efdad0052.js} +0 -0
  43. /package/dist/{4683.bundle.5d12928efa9f70f4.js → 4683.bundle.e5d3182f0f4347af.js} +0 -0
  44. /package/dist/{7295.css → 8492.css} +0 -0
  45. /package/dist/{8832.bundle.ede981706cac8e15.js → 8832.bundle.1cb57aab32daeab8.js} +0 -0
  46. /package/dist/{9116.bundle.225e003c28c550c1.js → 9116.bundle.fffb2ff349a3c452.js} +0 -0
  47. /package/dist/{9290.bundle.a18c40b82772a3c6.js → 9290.bundle.82cf4e74ab3fa20a.js} +0 -0
@@ -0,0 +1,400 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>OHIF URL Customizations</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light dark;
10
+ --bg: #f5f6f8;
11
+ --card: #ffffff;
12
+ --card-border: #e3e6ea;
13
+ --text: #1a1f27;
14
+ --muted: #5c6774;
15
+ --accent: #2563eb;
16
+ --accent-contrast: #ffffff;
17
+ --code-bg: #eef1f5;
18
+ --tag-bg: #e6edff;
19
+ --tag-text: #2952cc;
20
+ --bar-bg: #ffffff;
21
+ }
22
+ /* This catalog page follows the OS light/dark preference automatically. */
23
+ @media (prefers-color-scheme: dark) {
24
+ :root {
25
+ --bg: #0e1116;
26
+ --card: #171b22;
27
+ --card-border: #2a3038;
28
+ --text: #e6e9ee;
29
+ --muted: #99a3b0;
30
+ --accent: #4f8cff;
31
+ --accent-contrast: #0e1116;
32
+ --code-bg: #0b0e13;
33
+ --tag-bg: #1c2740;
34
+ --tag-text: #8fb2ff;
35
+ --bar-bg: #12161c;
36
+ }
37
+ }
38
+ * { box-sizing: border-box; }
39
+ body {
40
+ margin: 0;
41
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
42
+ background: var(--bg);
43
+ color: var(--text);
44
+ line-height: 1.5;
45
+ padding-bottom: 160px;
46
+ }
47
+ .wrap { max-width: 900px; margin: 0 auto; padding: 32px 20px 20px; }
48
+ .topbar { display: flex; align-items: flex-start; gap: 16px; }
49
+ .topbar .titles { flex: 1; min-width: 0; }
50
+ .theme-picker { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
51
+ .theme-picker label { font-size: 0.8rem; color: var(--muted); }
52
+ .theme-picker select {
53
+ background: var(--card); border: 1px solid var(--card-border); border-radius: 8px;
54
+ padding: 7px 10px; color: var(--text); font-size: 0.85rem; cursor: pointer;
55
+ }
56
+ header h1 { font-size: 1.7rem; margin: 0 0 6px; }
57
+ header p { color: var(--muted); margin: 0 0 8px; max-width: 62ch; }
58
+ code {
59
+ background: var(--code-bg);
60
+ border-radius: 4px;
61
+ padding: 1px 6px;
62
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
63
+ font-size: 0.85em;
64
+ }
65
+ .group { margin-top: 28px; }
66
+ .group-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
67
+ .group-head h2 { font-size: 1.15rem; margin: 0; }
68
+ .group-head .dir { color: var(--muted); font-size: 0.85rem; }
69
+ .group-head .toggle {
70
+ margin-left: auto; background: none; border: none; color: var(--accent);
71
+ cursor: pointer; font-size: 0.82rem; padding: 2px 4px;
72
+ }
73
+ .group-desc { color: var(--muted); font-size: 0.9rem; margin: 0 0 12px; }
74
+ .card {
75
+ display: flex; gap: 12px; align-items: flex-start;
76
+ background: var(--card); border: 1px solid var(--card-border);
77
+ border-radius: 10px; padding: 14px 16px; margin-bottom: 10px;
78
+ cursor: pointer; transition: border-color .12s ease;
79
+ }
80
+ .card:hover { border-color: var(--accent); }
81
+ .card input { margin-top: 3px; width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }
82
+ .card .body { flex: 1; min-width: 0; }
83
+ .card .title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
84
+ .card .name { font-weight: 600; }
85
+ .card .param { color: var(--muted); font-size: 0.8rem; }
86
+ .card .desc { color: var(--muted); font-size: 0.9rem; margin: 4px 0 0; }
87
+ .tag {
88
+ font-size: 0.7rem; font-weight: 600; letter-spacing: .02em;
89
+ background: var(--tag-bg); color: var(--tag-text);
90
+ padding: 2px 7px; border-radius: 999px; text-transform: uppercase;
91
+ }
92
+ .launch {
93
+ position: fixed; bottom: 0; left: 0; right: 0;
94
+ background: var(--bar-bg); border-top: 1px solid var(--card-border);
95
+ box-shadow: 0 -4px 16px rgba(0,0,0,0.08);
96
+ }
97
+ .launch-inner { max-width: 900px; margin: 0 auto; padding: 14px 20px; }
98
+ .launch-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
99
+ .launch-label { font-size: 0.8rem; color: var(--muted); }
100
+ .url-box {
101
+ flex: 1; min-width: 240px;
102
+ background: var(--code-bg); border: 1px solid var(--card-border); border-radius: 8px;
103
+ padding: 9px 12px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
104
+ font-size: 0.82rem; color: var(--text); overflow-x: auto; white-space: nowrap;
105
+ }
106
+ .base-input {
107
+ background: var(--card); border: 1px solid var(--card-border); border-radius: 8px;
108
+ padding: 8px 10px; color: var(--text); font-size: 0.85rem; width: 130px;
109
+ }
110
+ button.btn {
111
+ border: none; border-radius: 8px; padding: 9px 16px; font-size: 0.88rem; font-weight: 600;
112
+ cursor: pointer;
113
+ }
114
+ .btn-primary { background: var(--accent); color: var(--accent-contrast); }
115
+ .btn-primary[aria-disabled='true'] { opacity: .45; pointer-events: none; }
116
+ .btn-ghost { background: transparent; color: var(--accent); border: 1px solid var(--card-border); }
117
+ .count { font-size: 0.8rem; color: var(--muted); }
118
+ a { color: var(--accent); }
119
+ </style>
120
+ </head>
121
+ <body>
122
+ <div class="wrap">
123
+ <header>
124
+ <div class="topbar">
125
+ <div class="titles">
126
+ <h1>URL Customizations</h1>
127
+ </div>
128
+ <div class="theme-picker">
129
+ <label for="viewer-theme">Viewer theme</label>
130
+ <select id="viewer-theme" title="OHIF viewer theme — added as ?theme= to the launch URL">
131
+ <optgroup label="Tonal (surfaces carry the theme hue)">
132
+ <option value="default">OHIF Blue (default)</option>
133
+ <option value="orchid">Orchid</option>
134
+ <option value="arctic">Arctic</option>
135
+ <option value="verdant">Verdant</option>
136
+ </optgroup>
137
+ <optgroup label="Neutral (desaturated + one accent)">
138
+ <option value="midnight">Midnight</option>
139
+ <option value="slate">Slate</option>
140
+ <option value="deep">Deep</option>
141
+ </optgroup>
142
+ </select>
143
+ </div>
144
+ </div>
145
+ <p>
146
+ These are the example <code>?customization=</code> profiles shipped under
147
+ <code>platform/app/public/customizations/</code>, grouped by area. Each file is fetched as
148
+ JSONC <strong>data</strong> (never executed) and applied at startup. Tick the ones you want,
149
+ optionally pick a viewer theme (added as <code>?theme=</code>), then launch the viewer or copy
150
+ the generated query string onto any viewer URL. Multiple profiles are comma-separated and
151
+ applied in order.
152
+ </p>
153
+ <p style="font-size: 0.85rem">
154
+ See the
155
+ <a href="https://docs.ohif.org/platform/services/customization-service/specificCustomizations">
156
+ customization docs</a>
157
+ for how to author your own. The prefix allowlist is set by
158
+ <code>customizationUrlPrefixes</code> in the app config.
159
+ </p>
160
+ </header>
161
+
162
+ <main id="groups"></main>
163
+ </div>
164
+
165
+ <div class="launch">
166
+ <div class="launch-inner">
167
+ <div class="launch-row" style="margin-bottom: 10px">
168
+ <span class="launch-label">Viewer base</span>
169
+ <input
170
+ id="base"
171
+ class="base-input"
172
+ value="../"
173
+ title="Base viewer URL the customization query is appended to (relative to this page)"
174
+ />
175
+ <span class="count" id="count">0 selected</span>
176
+ <button class="btn btn-ghost" id="clear" type="button">Clear all</button>
177
+ <button class="btn btn-ghost" id="copy" type="button">Copy URL</button>
178
+ <a class="btn btn-primary" id="launch" href="#" aria-disabled="true">Launch viewer &rarr;</a>
179
+ </div>
180
+ <div class="launch-row">
181
+ <div class="url-box" id="url">Select one or more profiles above&hellip;</div>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <script>
187
+ // Catalog of shipped URL customizations, grouped by area/folder. The
188
+ // `value` is the `?customization=` token (folder-relative path, no `.jsonc`).
189
+ const CATALOG = [
190
+ {
191
+ dir: 'tools/',
192
+ title: 'Tools & presets',
193
+ desc: 'General viewport tools and presets. These apply globally (every mode).',
194
+ items: [
195
+ {
196
+ value: 'tools/ctPresets',
197
+ name: 'CT window/level presets',
198
+ scope: 'global',
199
+ desc: 'Replaces the CT presets in the window-level menu with a site-specific set (soft tissue, lung, angio, bone). Other modalities keep their defaults.',
200
+ },
201
+ {
202
+ value: 'tools/smoothRotate',
203
+ name: 'Smooth Rotate button',
204
+ scope: 'global',
205
+ desc: 'Adds a "Smooth Rotate" button to the More Tools menu that activates the PlanarRotate tool — drag to rotate the image freely (unlike the fixed 90° Rotate Right).',
206
+ },
207
+ {
208
+ value: 'tools/measurementLabels',
209
+ name: 'Predefined measurement labels',
210
+ scope: 'global',
211
+ desc: 'Prompts for a label from a fixed, exclusive list (Head, Shoulder, Knee, Toe) whenever a measurement is created.',
212
+ },
213
+ ],
214
+ },
215
+ {
216
+ dir: 'worklist/',
217
+ title: 'Study list',
218
+ desc: 'Customizations for the WorkList / study browser.',
219
+ items: [
220
+ {
221
+ value: 'worklist/patientBirthDate',
222
+ name: 'Birth Date column',
223
+ scope: 'global',
224
+ desc: 'Adds a "Birth Date" column to the study list, immediately after the Patient column. Shipped configs load this by default via customizationService.requires.',
225
+ },
226
+ ],
227
+ },
228
+ {
229
+ dir: 'segmentation/',
230
+ title: 'Segmentation',
231
+ desc: 'Per-mode composition of the segmentation capability packs.',
232
+ items: [
233
+ {
234
+ value: 'segmentation/segmentationEditing',
235
+ name: 'Segmentation editing',
236
+ scope: 'basic / longitudinal',
237
+ desc: 'Adds the segmentation editing toolbar, tools, and panels to the basic and longitudinal (viewer) modes, and enables editing in the segmentation panel.',
238
+ },
239
+ {
240
+ value: 'segmentation/segmentationAnnotationTools',
241
+ name: 'Annotation tools in segmentation mode',
242
+ scope: 'segmentation',
243
+ desc: 'Enables the measurement/annotation tools (Length, Bidirectional, ROIs, freehand, spline, livewire) inside the segmentation mode and adds the measurement panel.',
244
+ },
245
+ ],
246
+ },
247
+ {
248
+ dir: 'veterinary/',
249
+ title: 'Veterinary',
250
+ desc: 'Veterinary-oriented overlay and a chaining profile that pulls it in.',
251
+ items: [
252
+ {
253
+ value: 'veterinary/veterinary',
254
+ name: 'Veterinary profile',
255
+ scope: 'global',
256
+ desc: 'Chaining profile — declares requires: ["veterinary/veterinaryOverlay"], so selecting this one alone pulls in and applies the overlay first.',
257
+ },
258
+ {
259
+ value: 'veterinary/veterinaryOverlay',
260
+ name: 'Veterinary viewport overlay',
261
+ scope: 'global',
262
+ desc: 'Overrides the viewport overlay with a veterinary-style demographics layout (patient/ID/date on the left, species/breed on the right).',
263
+ },
264
+ ],
265
+ },
266
+ ];
267
+
268
+ const groupsEl = document.getElementById('groups');
269
+ const urlEl = document.getElementById('url');
270
+ const launchEl = document.getElementById('launch');
271
+ const countEl = document.getElementById('count');
272
+ const baseEl = document.getElementById('base');
273
+ const viewerThemeEl = document.getElementById('viewer-theme');
274
+
275
+ CATALOG.forEach((group, gi) => {
276
+ const section = document.createElement('section');
277
+ section.className = 'group';
278
+ section.innerHTML =
279
+ '<div class="group-head">' +
280
+ '<h2>' + group.title + '</h2>' +
281
+ '<span class="dir"><code>' + group.dir + '</code></span>' +
282
+ '<button class="toggle" type="button" data-group="' + gi + '">Select all</button>' +
283
+ '</div>' +
284
+ '<p class="group-desc">' + group.desc + '</p>';
285
+
286
+ group.items.forEach(item => {
287
+ const label = document.createElement('label');
288
+ label.className = 'card';
289
+ label.innerHTML =
290
+ '<input type="checkbox" value="' + item.value + '" />' +
291
+ '<span class="body">' +
292
+ '<span class="title-row">' +
293
+ '<span class="name">' + item.name + '</span>' +
294
+ '<span class="tag">' + item.scope + '</span>' +
295
+ '<span class="param"><code>?customization=' + item.value + '</code></span>' +
296
+ '</span>' +
297
+ '<span class="desc">' + item.desc + '</span>' +
298
+ '</span>';
299
+ section.appendChild(label);
300
+ });
301
+ groupsEl.appendChild(section);
302
+ });
303
+
304
+ function selectedValues() {
305
+ return Array.from(document.querySelectorAll('input[type=checkbox]:checked')).map(c => c.value);
306
+ }
307
+
308
+ // Builds the launch URL from the selected profiles and viewer theme. The
309
+ // `theme=` param is only added for a non-default preset (default is the
310
+ // viewer's built-in look and needs no param). Returns null when there is
311
+ // nothing to launch.
312
+ function buildUrl(base, values, theme) {
313
+ // Sanitize the DOM-derived inputs before they reach the launch href.
314
+ // Customization tokens are `folder/name` segments, so allow word chars,
315
+ // dot, dash and slash only; the theme is a plain preset id. Anything with
316
+ // query metacharacters or a URL scheme (e.g. `javascript:`) is dropped.
317
+ const TOKEN = /^[\w.-]+(?:\/[\w.-]+)*$/;
318
+ const safeValues = values.filter(v => TOKEN.test(v));
319
+ const safeTheme = /^[\w.-]+$/.test(theme || '') ? theme : 'default';
320
+ if (!safeValues.length && (!safeTheme || safeTheme === 'default')) {
321
+ return null;
322
+ }
323
+
324
+ // Resolve the (hand-typed) base against the current page and require an
325
+ // http(s) target, so the base can never turn the href into a
326
+ // javascript:/data: URL. searchParams.set() encodes the values safely.
327
+ let parsed;
328
+ try {
329
+ parsed = new URL(base, window.location.href);
330
+ } catch (_) {
331
+ return null;
332
+ }
333
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
334
+ return null;
335
+ }
336
+
337
+ if (safeValues.length) {
338
+ parsed.searchParams.set('customization', safeValues.join(','));
339
+ }
340
+ if (safeTheme && safeTheme !== 'default') {
341
+ parsed.searchParams.set('theme', safeTheme);
342
+ }
343
+ return parsed.toString();
344
+ }
345
+
346
+ function refresh() {
347
+ const values = selectedValues();
348
+ countEl.textContent = values.length + ' selected';
349
+ const url = buildUrl(baseEl.value.trim() || '../', values, viewerThemeEl.value);
350
+ if (!url) {
351
+ urlEl.textContent = 'Select one or more profiles, or a viewer theme, above…';
352
+ launchEl.setAttribute('aria-disabled', 'true');
353
+ launchEl.removeAttribute('href');
354
+ return;
355
+ }
356
+ urlEl.textContent = url;
357
+ launchEl.href = url;
358
+ launchEl.setAttribute('aria-disabled', 'false');
359
+ }
360
+
361
+ groupsEl.addEventListener('change', refresh);
362
+ baseEl.addEventListener('input', refresh);
363
+ viewerThemeEl.addEventListener('change', refresh);
364
+
365
+ groupsEl.addEventListener('click', e => {
366
+ const btn = e.target.closest('.toggle');
367
+ if (!btn) return;
368
+ e.preventDefault();
369
+ const section = btn.closest('.group');
370
+ const boxes = section.querySelectorAll('input[type=checkbox]');
371
+ const allChecked = Array.from(boxes).every(b => b.checked);
372
+ boxes.forEach(b => (b.checked = !allChecked));
373
+ btn.textContent = allChecked ? 'Select all' : 'Clear';
374
+ refresh();
375
+ });
376
+
377
+ document.getElementById('clear').addEventListener('click', () => {
378
+ document.querySelectorAll('input[type=checkbox]').forEach(b => (b.checked = false));
379
+ document.querySelectorAll('.toggle').forEach(t => (t.textContent = 'Select all'));
380
+ refresh();
381
+ });
382
+
383
+ document.getElementById('copy').addEventListener('click', async () => {
384
+ const text = buildUrl(baseEl.value.trim() || '../', selectedValues(), viewerThemeEl.value);
385
+ if (!text) return;
386
+ try {
387
+ await navigator.clipboard.writeText(text);
388
+ const b = document.getElementById('copy');
389
+ const prev = b.textContent;
390
+ b.textContent = 'Copied!';
391
+ setTimeout(() => (b.textContent = prev), 1200);
392
+ } catch (_) {
393
+ /* clipboard unavailable — the URL is visible in the box to copy manually */
394
+ }
395
+ });
396
+
397
+ refresh();
398
+ </script>
399
+ </body>
400
+ </html>
@@ -0,0 +1,48 @@
1
+ // URL-loaded customization: segmentationAnnotationTools
2
+ //
3
+ // Enables the measurement/annotation tools inside the segmentation mode by
4
+ // composing capability packs the cornerstone extension already exports:
5
+ //
6
+ // - the annotation buttons are already registered by the mode (its
7
+ // `toolbarButtons` composition includes `cornerstone.toolbarButtons`), so
8
+ // the toolbar only needs a `MeasurementTools` section added to the primary
9
+ // bar,
10
+ // - the annotation tools themselves are added to the mode's tool groups via
11
+ // `cornerstone.annotationTools`,
12
+ // - and the measurement panel is appended so annotations can be reviewed.
13
+ //
14
+ // Everything is per-mode, so it all lives in the `mode` phase keyed by the
15
+ // segmentation mode's route name. The section tweak patches the mode's toolbar
16
+ // layout pack at Mode scope (reset on mode exit), and the tools/panels `$push`
17
+ // onto the values the mode seeds on enter.
18
+ //
19
+ // Load it with `?customization=segmentation/segmentationAnnotationTools`.
20
+ {
21
+ "mode": {
22
+ "segmentation": {
23
+ "cornerstone.segmentationModeToolbarSections": {
24
+ "primary": { "$unshift": ["MeasurementTools"] },
25
+ "MeasurementTools": {
26
+ "$set": [
27
+ "Length",
28
+ "Bidirectional",
29
+ "ArrowAnnotate",
30
+ "EllipticalROI",
31
+ "RectangleROI",
32
+ "CircleROI",
33
+ "PlanarFreehandROI",
34
+ "SplineROI",
35
+ "LivewireContour"
36
+ ]
37
+ }
38
+ },
39
+ "toolGroupAdditions": {
40
+ "default": { "$push": [{ "$reference": "cornerstone.annotationTools" }] },
41
+ "mpr": { "$push": [{ "$reference": "cornerstone.annotationTools" }] }
42
+ },
43
+ "rightPanels": {
44
+ "$push": ["@ohif/extension-cornerstone.panelModule.panelMeasurement"]
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,59 @@
1
+ // URL-loaded customization: segmentationEditing
2
+ //
3
+ // Adds segmentation editing to the basic and longitudinal (viewer) modes by
4
+ // composing the capability packs the cornerstone extension already exports:
5
+ //
6
+ // - the segmentation editing toolbar buttons and toolbox section wiring
7
+ // (`cornerstone.segmentationToolbarButtons` / `cornerstone.segmentationToolbarSections`),
8
+ // - the segmentation editing tools for the modes' tool groups
9
+ // (`cornerstone.segmentationTools`),
10
+ // - the segmentation panels that render the editing toolbox
11
+ // (`panelSegmentationWithToolsLabelMap` / `panelSegmentationWithToolsContour`),
12
+ // - and enables editing in the segmentation panel.
13
+ //
14
+ // Composition is per-mode, so every patch lives in the `mode` phase keyed by
15
+ // the target mode's route name (`basic`, `viewer`). Each mode seeds its own
16
+ // `toolbarButtons` / `toolbarSections` / `toolGroupAdditions` / `rightPanels`
17
+ // onto the Mode scope on enter; these blocks `$push` / `$set` on top of that,
18
+ // exactly like the panel swaps. No app-wide `global` keys are needed.
19
+ //
20
+ // Load it with `?customization=segmentation/segmentationEditing`.
21
+ {
22
+ "mode": {
23
+ "basic": {
24
+ "toolbarButtons": { "$push": [{ "$reference": "cornerstone.segmentationToolbarButtons" }] },
25
+ "toolbarSections": { "$push": [{ "$reference": "cornerstone.segmentationToolbarSections" }] },
26
+ "toolGroupAdditions": {
27
+ "default": { "$push": [{ "$reference": "cornerstone.segmentationTools" }] },
28
+ "mpr": { "$push": [{ "$reference": "cornerstone.segmentationTools" }] }
29
+ },
30
+ // The basic/longitudinal modes seed `panelSegmentation.disableEditing: true`
31
+ // from their own `modeCustomizations`; this `mode` phase value is applied
32
+ // after that (both Mode scope) and enables editing.
33
+ "panelSegmentation.disableEditing": { "$set": false },
34
+ "rightPanels": {
35
+ "$set": [
36
+ "@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap",
37
+ "@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour",
38
+ "@ohif/extension-cornerstone.panelModule.panelMeasurement"
39
+ ]
40
+ }
41
+ },
42
+ "viewer": {
43
+ "toolbarButtons": { "$push": [{ "$reference": "cornerstone.segmentationToolbarButtons" }] },
44
+ "toolbarSections": { "$push": [{ "$reference": "cornerstone.segmentationToolbarSections" }] },
45
+ "toolGroupAdditions": {
46
+ "default": { "$push": [{ "$reference": "cornerstone.segmentationTools" }] },
47
+ "mpr": { "$push": [{ "$reference": "cornerstone.segmentationTools" }] }
48
+ },
49
+ "panelSegmentation.disableEditing": { "$set": false },
50
+ "rightPanels": {
51
+ "$set": [
52
+ "@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap",
53
+ "@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour",
54
+ "@ohif/extension-measurement-tracking.panelModule.trackedMeasurements"
55
+ ]
56
+ }
57
+ }
58
+ }
59
+ }
@@ -4,7 +4,7 @@
4
4
  // site-specific set (key: `cornerstone.windowLevelPresets`). Other modalities
5
5
  // keep their defaults because only the `CT` entry is overridden.
6
6
  //
7
- // Load it with `?customization=ctPresets`.
7
+ // Load it with `?customization=tools/ctPresets`.
8
8
  {
9
9
  "global": {
10
10
  "cornerstone.windowLevelPresets": {
@@ -4,7 +4,7 @@
4
4
  // is created (key: `measurementLabels`). `labelOnMeasure` triggers the prompt on
5
5
  // creation and `exclusive` restricts entries to the provided items.
6
6
  //
7
- // Load it with `?customization=measurementLabels`.
7
+ // Load it with `?customization=tools/measurementLabels`.
8
8
  {
9
9
  "global": {
10
10
  "measurementLabels": {
@@ -8,7 +8,7 @@
8
8
  // extension (`cornerstone.toolbarButtons` / `cornerstone.toolbarSections`) using
9
9
  // immutability-helper `$push` commands, so the built-in buttons are preserved.
10
10
  //
11
- // Load it with `?customization=smoothRotate`.
11
+ // Load it with `?customization=tools/smoothRotate`.
12
12
  {
13
13
  "global": {
14
14
  "cornerstone.toolbarButtons": {
@@ -0,0 +1,5 @@
1
+ // Example chaining module: ensures `veterinary/veterinaryOverlay` is loaded and
2
+ // applied first when using `?customization=veterinary/veterinary` alone.
3
+ {
4
+ "requires": ["veterinary/veterinaryOverlay"]
5
+ }
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Demonstrates a runtime-loaded customization that overrides the default
4
4
  // viewport overlay with a veterinary-style demographics layout. Loaded via
5
- // `?customization=veterinaryOverlay` (see CustomizationService URL handling).
5
+ // `?customization=veterinary/veterinaryOverlay` (see CustomizationService URL handling).
6
6
  //
7
7
  // Uses the same `global`-at-top-level shape as the cornerstone overlay samples
8
8
  // and `inheritsFrom: 'ohif.overlayItem'` on each row, matching
@@ -39,14 +39,14 @@
39
39
  {
40
40
  "id": "PatientSpecies",
41
41
  "inheritsFrom": "ohif.overlayItem",
42
- "attribute": "PatientSpecies",
42
+ "attribute": "PatientSpeciesDescription",
43
43
  "label": "Species",
44
44
  "title": "Patient species"
45
45
  },
46
46
  {
47
47
  "id": "PatientBreed",
48
48
  "inheritsFrom": "ohif.overlayItem",
49
- "attribute": "PatientBreed",
49
+ "attribute": "PatientBreedDescription",
50
50
  "label": "Breed",
51
51
  "title": "Patient breed"
52
52
  }
@@ -8,8 +8,8 @@
8
8
  // tag 00100030).
9
9
  //
10
10
  // This applies in the `global` phase, so it is in place before the WorkList
11
- // renders. Load it with `?customization=patientBirthDate`, or pull it in from
12
- // `appConfig.customizationService.requires`.
11
+ // renders. Load it with `?customization=worklist/patientBirthDate`, or pull it in
12
+ // from `appConfig.customizationService.requires`.
13
13
  {
14
14
  "global": {
15
15
  "workList.columns": {
package/dist/index.html CHANGED
@@ -1,3 +1,3 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no,maximum-scale=1.0,user-scalable=no"><meta name="theme-color" content="#000000"><meta name="mobile-web-app-capable" content="yes"><meta name="application-name" content="OHIF Viewer"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="@ohif/app"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><script>console.time("scriptToView"),window.PUBLIC_URL="/"</script><link rel="manifest" href="/manifest.json"><link rel="icon shortcut" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-320x460.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-640x920.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-640x1096.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-750x1294.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 3)" href="/assets/apple-touch-startup-image-1182x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 3)" href="/assets/apple-touch-startup-image-1242x2148.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-748x1024.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-768x1004.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-1496x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-1536x2008.png"><link rel="icon" type="image/png" sizes="228x228" href="/assets/coast-228x228.png"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script rel="preload" as="script" src="/app-config.js"></script><script rel="preload" as="script" type="module" src="/init-service-worker.js"></script><title>OHIF Viewer</title><script>function browserImportFunction(n){return import(n)}window.PUBLIC_URL="/"</script><script defer src="/app.bundle.55057e411d635dc3.js"></script><link href="/app.bundle.css" rel="stylesheet"></head><body><noscript> You need to enable JavaScript to run this app. </noscript>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no,maximum-scale=1.0,user-scalable=no"><meta name="theme-color" content="#000000"><meta name="mobile-web-app-capable" content="yes"><meta name="application-name" content="OHIF Viewer"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="@ohif/app"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><script>console.time("scriptToView"),window.PUBLIC_URL="/"</script><link rel="manifest" href="/manifest.json"><link rel="icon shortcut" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-320x460.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-640x920.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-640x1096.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-750x1294.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 3)" href="/assets/apple-touch-startup-image-1182x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 3)" href="/assets/apple-touch-startup-image-1242x2148.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-748x1024.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)" href="/assets/apple-touch-startup-image-768x1004.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-1496x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" href="/assets/apple-touch-startup-image-1536x2008.png"><link rel="icon" type="image/png" sizes="228x228" href="/assets/coast-228x228.png"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script rel="preload" as="script" src="/app-config.js"></script><script rel="preload" as="script" type="module" src="/init-service-worker.js"></script><title>OHIF Viewer</title><script>function browserImportFunction(n){return import(n)}window.PUBLIC_URL="/"</script><script defer src="/app.bundle.a73ad7e598640adf.js"></script><link href="/app.bundle.css" rel="stylesheet"></head><body><noscript> You need to enable JavaScript to run this app. </noscript>
2
2
  <div id="react-portal"></div>
3
3
  <div id="root"></div></body></html>