matterviz 0.1.8 → 0.1.10

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 (105) hide show
  1. package/dist/ContextMenu.svelte +1 -1
  2. package/dist/ContextMenu.svelte.d.ts +3 -3
  3. package/dist/DraggablePane.svelte +38 -38
  4. package/dist/DraggablePane.svelte.d.ts +0 -1
  5. package/dist/FilePicker.svelte +42 -60
  6. package/dist/FilePicker.svelte.d.ts +3 -4
  7. package/dist/Icon.svelte +1 -1
  8. package/dist/Icon.svelte.d.ts +2 -2
  9. package/dist/InfoCard.svelte +4 -4
  10. package/dist/InfoCard.svelte.d.ts +4 -5
  11. package/dist/SettingsSection.svelte +2 -2
  12. package/dist/SettingsSection.svelte.d.ts +2 -2
  13. package/dist/Spinner.svelte +3 -3
  14. package/dist/Spinner.svelte.d.ts +2 -2
  15. package/dist/api/optimade.d.ts +2 -2
  16. package/dist/app.css +11 -8
  17. package/dist/colors/index.d.ts +9 -1
  18. package/dist/colors/index.js +19 -6
  19. package/dist/composition/BarChart.svelte +10 -6
  20. package/dist/composition/BarChart.svelte.d.ts +2 -2
  21. package/dist/composition/BubbleChart.svelte +3 -3
  22. package/dist/composition/BubbleChart.svelte.d.ts +2 -2
  23. package/dist/composition/Composition.svelte +24 -13
  24. package/dist/composition/Composition.svelte.d.ts +2 -2
  25. package/dist/composition/PieChart.svelte +2 -2
  26. package/dist/composition/PieChart.svelte.d.ts +2 -2
  27. package/dist/element/BohrAtom.svelte +10 -5
  28. package/dist/element/BohrAtom.svelte.d.ts +2 -2
  29. package/dist/element/ElementHeading.svelte.d.ts +2 -3
  30. package/dist/element/ElementPhoto.svelte +4 -9
  31. package/dist/element/ElementPhoto.svelte.d.ts +2 -2
  32. package/dist/element/ElementStats.svelte.d.ts +2 -2
  33. package/dist/element/ElementTile.svelte +19 -34
  34. package/dist/element/ElementTile.svelte.d.ts +6 -6
  35. package/dist/icons.d.ts +12 -0
  36. package/dist/icons.js +12 -0
  37. package/dist/index.d.ts +7 -0
  38. package/dist/labels.d.ts +2 -0
  39. package/dist/labels.js +39 -0
  40. package/dist/material/MaterialCard.svelte +4 -5
  41. package/dist/material/MaterialCard.svelte.d.ts +2 -2
  42. package/dist/material/index.d.ts +0 -1
  43. package/dist/material/index.js +0 -1
  44. package/dist/math.d.ts +2 -0
  45. package/dist/math.js +24 -12
  46. package/dist/periodic-table/PeriodicTable.svelte +1 -1
  47. package/dist/periodic-table/PeriodicTable.svelte.d.ts +2 -2
  48. package/dist/periodic-table/PropertySelect.svelte +2 -3
  49. package/dist/periodic-table/PropertySelect.svelte.d.ts +2 -3
  50. package/dist/periodic-table/TableInset.svelte +3 -3
  51. package/dist/periodic-table/TableInset.svelte.d.ts +3 -3
  52. package/dist/plot/ColorBar.svelte +21 -29
  53. package/dist/plot/ColorBar.svelte.d.ts +6 -6
  54. package/dist/plot/ColorScaleSelect.svelte +1 -1
  55. package/dist/plot/ColorScaleSelect.svelte.d.ts +4 -4
  56. package/dist/plot/ElementScatter.svelte.d.ts +4 -4
  57. package/dist/plot/Histogram.svelte +34 -18
  58. package/dist/plot/Histogram.svelte.d.ts +3 -3
  59. package/dist/plot/HistogramControls.svelte +1 -2
  60. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  61. package/dist/plot/Line.svelte.d.ts +2 -2
  62. package/dist/plot/PlotLegend.svelte +3 -3
  63. package/dist/plot/PlotLegend.svelte.d.ts +2 -2
  64. package/dist/plot/ScatterPlot.svelte +21 -24
  65. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  66. package/dist/plot/ScatterPlotControls.svelte +4 -6
  67. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  68. package/dist/plot/ScatterPoint.svelte.d.ts +2 -2
  69. package/dist/plot/data-transform.d.ts +3 -3
  70. package/dist/plot/data-transform.js +13 -13
  71. package/dist/settings.d.ts +2 -0
  72. package/dist/settings.js +10 -0
  73. package/dist/structure/CanvasTooltip.svelte +3 -3
  74. package/dist/structure/CanvasTooltip.svelte.d.ts +2 -2
  75. package/dist/structure/Lattice.svelte +5 -3
  76. package/dist/structure/Lattice.svelte.d.ts +4 -3
  77. package/dist/structure/Structure.svelte +40 -53
  78. package/dist/structure/Structure.svelte.d.ts +12 -10
  79. package/dist/structure/StructureControls.svelte +139 -72
  80. package/dist/structure/StructureControls.svelte.d.ts +3 -12
  81. package/dist/structure/StructureInfoPane.svelte +94 -33
  82. package/dist/structure/StructureInfoPane.svelte.d.ts +5 -3
  83. package/dist/structure/StructureLegend.svelte +9 -8
  84. package/dist/structure/StructureLegend.svelte.d.ts +2 -2
  85. package/dist/structure/StructureScene.svelte +438 -376
  86. package/dist/structure/StructureScene.svelte.d.ts +6 -3
  87. package/dist/structure/measure.d.ts +1 -1
  88. package/dist/structure/measure.js +87 -17
  89. package/dist/structure/parse.js +155 -79
  90. package/dist/structure/pbc.js +3 -0
  91. package/dist/symmetry/WyckoffTable.svelte +85 -0
  92. package/dist/symmetry/WyckoffTable.svelte.d.ts +10 -0
  93. package/dist/symmetry/index.d.ts +17 -0
  94. package/dist/symmetry/index.js +121 -0
  95. package/dist/theme/ThemeControl.svelte +2 -3
  96. package/dist/theme/ThemeControl.svelte.d.ts +2 -2
  97. package/dist/theme/themes.js +54 -88
  98. package/dist/trajectory/Trajectory.svelte +11 -10
  99. package/dist/trajectory/Trajectory.svelte.d.ts +2 -2
  100. package/dist/trajectory/TrajectoryInfoPane.svelte +14 -22
  101. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +1 -2
  102. package/dist/trajectory/plotting.js +9 -8
  103. package/package.json +16 -15
  104. package/dist/material/SymmetryCard.svelte +0 -11
  105. package/dist/material/SymmetryCard.svelte.d.ts +0 -8
@@ -52,7 +52,7 @@ function handle_option_click(section_title, option) {
52
52
  {#if visible}
53
53
  {@const { x, y } = get_smart_position()}
54
54
  {@const style = `position: absolute; left: ${x}px; top: ${y}px; ${rest.style ?? ``}`}
55
- <div class="context-menu" {...rest} {style} bind:this={menu_element}>
55
+ <div {...rest} class="context-menu {rest.class ?? ``}" {style} bind:this={menu_element}>
56
56
  {#each sections as { title, options } (title)}
57
57
  <div class="section">
58
58
  <div class="header">{title}</div>
@@ -1,10 +1,11 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
1
2
  interface MenuOption {
2
3
  value: string;
3
4
  icon?: string;
4
5
  label?: string;
5
6
  disabled?: boolean;
6
7
  }
7
- interface Props {
8
+ interface Props extends HTMLAttributes<HTMLDivElement> {
8
9
  sections: Readonly<{
9
10
  title: string;
10
11
  options: readonly MenuOption[];
@@ -17,8 +18,7 @@ interface Props {
17
18
  };
18
19
  visible: boolean;
19
20
  on_close?: () => void;
20
- menu_element?: HTMLElement;
21
- [key: string]: unknown;
21
+ menu_element?: HTMLDivElement;
22
22
  }
23
23
  declare const ContextMenu: import("svelte").Component<Props, {}, "menu_element">;
24
24
  type ContextMenu = ReturnType<typeof ContextMenu>;
@@ -1,15 +1,8 @@
1
1
  <script lang="ts">import { Icon } from './';
2
2
  import { draggable, tooltip } from 'svelte-multiselect/attachments';
3
- let { show = $bindable(false), show_pane = true, children, toggle_props = {}, open_icon = `Cross`, closed_icon = `Settings`, icon_style = ``, offset = { x: 5, y: 5 }, max_width = `450px`, pane_props = {}, onclose = () => { }, on_drag_start = () => { }, custom_toggle = undefined, toggle_pane_btn = $bindable(undefined), pane_div = $bindable(undefined), has_been_dragged = $bindable(false), currently_dragging = $bindable(false), } = $props();
3
+ let { show = $bindable(false), show_pane = true, children, toggle_props = {}, open_icon = `Cross`, closed_icon = `Settings`, icon_style = ``, offset = { x: 5, y: 5 }, max_width = `450px`, pane_props = {}, onclose = () => { }, on_drag_start = () => { }, toggle_pane_btn = $bindable(undefined), pane_div = $bindable(undefined), has_been_dragged = $bindable(false), currently_dragging = $bindable(false), } = $props();
4
4
  let initial_position = $state({ left: `50px`, top: `50px` });
5
5
  let show_control_buttons = $state(false);
6
- // Keyboard shortcuts
7
- function on_keydown(event) {
8
- if (event.key === `Escape`) {
9
- event.preventDefault();
10
- close_pane();
11
- }
12
- }
13
6
  function toggle_pane() {
14
7
  show = !show;
15
8
  if (!show)
@@ -52,8 +45,7 @@ function calculate_position() {
52
45
  const pane_width = pane_div?.getBoundingClientRect().width || 450;
53
46
  const positioned_ancestor = toggle_pane_btn.offsetParent;
54
47
  const ancestor_rect = positioned_ancestor?.getBoundingClientRect();
55
- if (!ancestor_rect) {
56
- // Fallback to document positioning
48
+ if (!ancestor_rect) { // Fallback to document positioning
57
49
  const scroll_x = window.scrollX || document.documentElement.scrollLeft;
58
50
  const scroll_y = window.scrollY || document.documentElement.scrollTop;
59
51
  return {
@@ -61,11 +53,9 @@ function calculate_position() {
61
53
  top: `${toggle_rect.bottom + (offset.y ?? 5) + scroll_y}px`,
62
54
  };
63
55
  }
64
- // Position relative to positioned ancestor
65
- return {
66
- left: `${toggle_rect.right - ancestor_rect.left - pane_width + (offset.x ?? 5)}px`,
67
- top: `${toggle_rect.bottom - ancestor_rect.top + (offset.y ?? 5)}px`,
68
- };
56
+ const left = `${toggle_rect.right - ancestor_rect.left - pane_width + (offset.x ?? 5)}px`;
57
+ const top = `${toggle_rect.bottom - ancestor_rect.top + (offset.y ?? 5)}px`;
58
+ return { left, top }; // Position relative to positioned ancestor
69
59
  }
70
60
  // Click outside handler
71
61
  function handle_click_outside(event) {
@@ -79,11 +69,6 @@ function handle_click_outside(event) {
79
69
  if (!is_toggle_button && !is_inside_pane && !currently_dragging)
80
70
  close_pane();
81
71
  }
82
- // Button click handler
83
- const handle_button_click = (action) => (event) => {
84
- event.stopPropagation();
85
- action();
86
- };
87
72
  // Debounced resize handler for better performance
88
73
  let resize_timeout = $state(undefined);
89
74
  function handle_resize() {
@@ -121,16 +106,23 @@ $effect(() => {
121
106
  });
122
107
  </script>
123
108
 
124
- <svelte:window onkeydown={on_keydown} onresize={handle_resize} />
109
+ <svelte:window
110
+ onkeydown={(event: KeyboardEvent) => {
111
+ if (event.key !== `Escape`) return
112
+ event.preventDefault()
113
+ close_pane()
114
+ }}
115
+ onresize={handle_resize}
116
+ />
125
117
  <svelte:document onclick={handle_click_outside} />
126
118
 
127
119
  {#if show_pane}
128
120
  <button
129
121
  type="button"
130
122
  bind:this={toggle_pane_btn}
131
- onclick={handle_button_click(custom_toggle || toggle_pane)}
132
123
  aria-expanded={show}
133
124
  {...toggle_props}
125
+ onclick={toggle_pane}
134
126
  class="pane-toggle {toggle_props.class ?? ``}"
135
127
  {@attach tooltip({ content: toggle_props.title ?? (show ? `Close pane` : `Open pane`) })}
136
128
  >
@@ -159,7 +151,7 @@ $effect(() => {
159
151
  <button
160
152
  type="button"
161
153
  class="reset-button"
162
- onclick={handle_button_click(reset_position)}
154
+ onclick={reset_position}
163
155
  title="Reset pane position"
164
156
  aria-label="Reset pane position"
165
157
  >
@@ -168,7 +160,7 @@ $effect(() => {
168
160
  <button
169
161
  type="button"
170
162
  class="close-button"
171
- onclick={handle_button_click(close_pane)}
163
+ onclick={close_pane}
172
164
  title="Close pane"
173
165
  aria-label="Close pane"
174
166
  >
@@ -194,8 +186,8 @@ $effect(() => {
194
186
  padding: var(--pane-toggle-padding, 2pt);
195
187
  border-radius: var(--pane-toggle-border-radius, 3pt);
196
188
  background-color: transparent;
197
- transition: background-color 0.2s;
198
- font-size: var(--pane-toggle-font-size, clamp(1.1em, calc(1cqw + 1cqh), 1.4em));
189
+ transition: var(--pane-toggle-transition, background-color 0.2s);
190
+ font-size: var(--pane-toggle-font-size, clamp(0.9em, 2cqmin, 1.4em));
199
191
  }
200
192
  button.pane-toggle:hover {
201
193
  background-color: color-mix(in srgb, currentColor 8%, transparent);
@@ -204,21 +196,25 @@ $effect(() => {
204
196
  position: absolute; /* Use absolute so pane scrolls with page content */
205
197
  background: var(--pane-bg, var(--page-bg, light-dark(white, black)));
206
198
  border: var(--pane-border, 1px solid rgba(255, 255, 255, 0.15));
207
- border-radius: 6px;
199
+ border-radius: var(--pane-border-radius, 6px);
208
200
  padding: var(--pane-padding, 1ex);
209
201
  box-sizing: border-box;
210
202
  z-index: var(--pane-z-index, 10);
211
203
  display: grid;
212
- gap: 4pt;
204
+ gap: var(--pane-gap, 4pt);
213
205
  text-align: left;
214
206
  /* Exclude position from being transitioned to prevent sluggish dragging */
215
207
  transition: opacity 0.3s, background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
216
208
  width: 28em;
217
- max-width: 90cqw;
218
- overflow-x: hidden;
219
- overflow-y: auto;
220
- max-height: min(90vh, 800px);
221
- pointer-events: auto;
209
+ max-width: var(--pane-max-width, 80cqw);
210
+ overflow-x: var(--pane-overflow-x, hidden);
211
+ overflow-y: var(--pane-overflow-y, auto);
212
+ min-height: min(
213
+ var(--pane-min-height, 400px),
214
+ calc(100cqh - var(--pane-bottom-margin, 40px))
215
+ ); /* Ensure pane never exceeds its query container, enabling internal scroll */
216
+ max-height: var(--pane-max-height, calc(100cqh - var(--pane-bottom-margin, 40px)));
217
+ overscroll-behavior: contain; /* Prevent scroll chaining to parent containers (e.g. Jupyter cells) */
222
218
  }
223
219
  :global(body.fullscreen) .draggable-pane {
224
220
  position: fixed !important; /* In fullscreen, we want viewport-relative positioning */
@@ -228,23 +224,26 @@ $effect(() => {
228
224
  }
229
225
  /* Pane content styling */
230
226
  .draggable-pane :global(h4) {
231
- margin: 2pt 0;
232
- font-size: 0.95em;
227
+ margin: var(--pane-h4-margin, 2pt 0);
228
+ font-size: var(--pane-h4-font-size, 0.95em);
233
229
  }
234
230
  .draggable-pane :global(hr) {
235
231
  border: none;
236
232
  background: var(--pane-hr-bg, rgba(255, 255, 255, 0.1));
237
- margin: 4pt 0;
233
+ margin: var(--pane-hr-margin, 4pt 0);
238
234
  height: 1px;
239
235
  }
236
+ .draggable-pane :global(> section > div) {
237
+ text-align: right; /* right align long line-breaking trajectory file names */
238
+ }
240
239
  .draggable-pane :global(label) {
241
240
  display: flex;
242
241
  align-items: center;
243
- gap: 2pt;
242
+ gap: var(--pane-label-gap, 2pt);
244
243
  }
245
244
  .draggable-pane :global(input[type='text']) {
246
245
  flex: 1;
247
- padding: 4px 6px;
246
+ padding: var(--pane-input-padding, 4px 6px);
248
247
  margin: var(--pane-input-margin, 0 0 0 5pt);
249
248
  }
250
249
  .draggable-pane :global(input[type='text'].invalid) {
@@ -321,6 +320,7 @@ $effect(() => {
321
320
  margin-bottom: calc(-2 * 12pt);
322
321
  box-sizing: border-box;
323
322
  justify-self: end;
323
+ z-index: var(--pane-control-buttons-z-index, 1);
324
324
  }
325
325
  .draggable-pane :global(.drag-handle) {
326
326
  width: 1.3em;
@@ -17,7 +17,6 @@ interface Props {
17
17
  pane_props?: HTMLAttributes<HTMLDivElement>;
18
18
  onclose?: () => void;
19
19
  on_drag_start?: () => void;
20
- custom_toggle?: () => void;
21
20
  toggle_pane_btn?: HTMLButtonElement;
22
21
  pane_div?: HTMLDivElement;
23
22
  has_been_dragged?: boolean;
@@ -1,4 +1,5 @@
1
- <script lang="ts">let { files, active_files = [], show_category_filters = false, category_labels = {}, on_drag_start, on_drag_end, type_mapper, file_type_colors = {
1
+ <script lang="ts">import { tooltip } from 'svelte-multiselect';
2
+ let { files, active_files = [], show_category_filters = false, on_drag_start, on_drag_end, type_mapper, file_type_colors = {
2
3
  cif: `rgba(100, 149, 237, 0.8)`,
3
4
  xyz: `rgba(50, 205, 50, 0.8)`,
4
5
  extxyz: `rgba(50, 205, 50, 0.8)`,
@@ -14,23 +15,29 @@
14
15
  let active_category_filter = $state(null);
15
16
  let active_type_filter = $state(null);
16
17
  // Helper function to get the base file type (removing .gz extension)
17
- const get_base_file_type = (filename) => {
18
+ const get_base_file_type = (file) => {
18
19
  // Use custom type mapper if provided
19
20
  if (type_mapper)
20
- return type_mapper(filename);
21
- let base_name = filename.toLowerCase();
21
+ return type_mapper(file);
22
+ let base_name = file.name.toLowerCase();
22
23
  // Remove .gz extension if present
23
24
  if (base_name.endsWith(`.gz`))
24
25
  base_name = base_name.slice(0, -3);
25
26
  return base_name.split(`.`).pop() || `file`;
26
27
  };
28
+ // Helper function to create normalized category identifier for filtering
29
+ const get_category_id = (file) => {
30
+ if (!file.category)
31
+ return `(uncategorized)`;
32
+ return `${file.category_icon ?? ``} ${file.category}`.trim();
33
+ };
27
34
  // Filter files based on active filters
28
35
  let filtered_files = $derived(files.filter((file) => {
29
- if (active_category_filter && file.category) {
30
- return file.category === active_category_filter;
36
+ if (active_category_filter) {
37
+ return get_category_id(file) === active_category_filter;
31
38
  }
32
39
  if (active_type_filter) {
33
- const normalized_type = get_base_file_type(file.name);
40
+ const normalized_type = get_base_file_type(file);
34
41
  return normalized_type === active_type_filter;
35
42
  }
36
43
  return true;
@@ -50,7 +57,7 @@ const handle_drag_start = (file) => (event) => {
50
57
  const payload = JSON.stringify({
51
58
  name: file.name,
52
59
  url: file_url,
53
- type: file.type || get_base_file_type(file.name),
60
+ type: file.type || get_base_file_type(file),
54
61
  category: file.category,
55
62
  });
56
63
  // Set file data as JSON for applications that can handle it
@@ -59,37 +66,32 @@ const handle_drag_start = (file) => (event) => {
59
66
  event.dataTransfer?.setData(`text/plain`, file_url);
60
67
  on_drag_start?.(file, event);
61
68
  };
62
- // Get unique file types for format filters
63
- let uniq_formats = $derived([...new Set(files.map((file) => get_base_file_type(file.name)))].sort());
64
- // Get unique category types for category filters
65
- let uniq_categories = $derived(show_category_filters
66
- ? [...new Set(files.map((file) => file.category))].sort().filter(Boolean)
67
- : []);
68
- export {};
69
+ // Get unique file types/categories for format/category filters
70
+ let uniq_formats = $derived([...new Set(files.map(get_base_file_type))].sort());
71
+ let uniq_categories = $derived([...new Set(files.map(get_category_id))].filter(Boolean).sort());
69
72
  </script>
70
73
 
71
74
  <div class="file-picker" {...rest}>
72
75
  <div class="legend">
73
- {#if show_category_filters}
74
- {#each uniq_categories as category (category)}
75
- {@const is_active = active_category_filter === category}
76
- <span
77
- class="legend-item"
78
- class:active={is_active}
79
- onclick={() => category && toggle_filter(`category`, category)}
80
- onkeydown={(evt) =>
81
- (evt.key === `Enter` || evt.key === ` `) &&
82
- category &&
83
- toggle_filter(`category`, category)}
84
- role="button"
85
- tabindex="0"
86
- title="Filter to show only {category}"
87
- >
88
- {(category && category_labels[category]) || category}
89
- </span>
90
- {/each}
91
- {#if uniq_categories.length > 0 && uniq_formats.length > 0}&emsp;{/if}
92
- {/if}
76
+ {#each show_category_filters ? uniq_categories : [] as category (category)}
77
+ {@const is_active = active_category_filter === category}
78
+ <span
79
+ class="legend-item"
80
+ class:active={is_active}
81
+ onclick={() => category && toggle_filter(`category`, category)}
82
+ onkeydown={(evt) =>
83
+ (evt.key === `Enter` || evt.key === ` `) &&
84
+ category &&
85
+ toggle_filter(`category`, category)}
86
+ role="button"
87
+ tabindex="0"
88
+ aria-pressed={is_active}
89
+ {@attach tooltip({ content: `Filter to show only ${category}` })}
90
+ >
91
+ {category}
92
+ </span>
93
+ {/each}
94
+ {#if uniq_categories.length > 0 && uniq_formats.length > 0}&emsp;{/if}
93
95
 
94
96
  {#each uniq_formats as format (format)}
95
97
  {@const is_active = active_type_filter === format}
@@ -101,7 +103,7 @@ export {};
101
103
  (evt.key === `Enter` || evt.key === ` `) && toggle_filter(`type`, format)}
102
104
  role="button"
103
105
  tabindex="0"
104
- title="Filter to show only {format.toUpperCase()} files"
106
+ {@attach tooltip({ content: `Filter to show only ${format.toUpperCase()} files` })}
105
107
  >
106
108
  <span
107
109
  class="format-circle"
@@ -112,12 +114,9 @@ export {};
112
114
 
113
115
  {#if active_category_filter || active_type_filter}
114
116
  <button
115
- title="Clear all filters"
117
+ {@attach tooltip({ content: `Clear all filters` })}
116
118
  class="clear-filter"
117
- onclick={() => {
118
- active_category_filter = null
119
- active_type_filter = null
120
- }}
119
+ onclick={() => [active_category_filter, active_type_filter] = [null, null]}
121
120
  >
122
121
 
123
122
  </button>
@@ -125,7 +124,7 @@ export {};
125
124
  </div>
126
125
 
127
126
  {#each filtered_files as file (file.name)}
128
- {@const base_type = get_base_file_type(file.name)}
127
+ {@const base_type = get_base_file_type(file)}
129
128
  {@const is_compressed = file.name.toLowerCase().endsWith(`.gz`)}
130
129
  <div
131
130
  class="file-item"
@@ -139,13 +138,8 @@ export {};
139
138
  tabindex="0"
140
139
  title="Drag this {base_type.toUpperCase()} file"
141
140
  >
142
- <div class="drag-handle">
143
- <div class="drag-bar"></div>
144
- <div class="drag-bar"></div>
145
- <div class="drag-bar"></div>
146
- </div>
147
141
  <div class="file-name">
148
- {file.name}{file.category ? `\u00A0${file.category}` : ``}
142
+ {file.category ? `${file.category_icon} ` : ``}{file.name}
149
143
  {#if is_compressed}<span class="compression-indicator">📦</span>{/if}
150
144
  </div>
151
145
  </div>
@@ -231,18 +225,6 @@ export {};
231
225
  background: rgba(0, 122, 204, 0.2);
232
226
  filter: brightness(1.1);
233
227
  }
234
- .drag-handle {
235
- display: flex;
236
- flex-direction: column;
237
- gap: 2px;
238
- opacity: 0.6;
239
- }
240
- .drag-bar {
241
- width: 12px;
242
- height: 2px;
243
- background: currentColor;
244
- border-radius: 1px;
245
- }
246
228
  .file-name {
247
229
  font-size: 0.7em;
248
230
  line-height: 1.1;
@@ -1,14 +1,13 @@
1
1
  import type { FileInfo } from './';
2
- interface Props {
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
4
  files: FileInfo[];
4
5
  active_files?: string[];
5
6
  show_category_filters?: boolean;
6
- category_labels?: Record<string, string>;
7
7
  on_drag_start?: (file: FileInfo, event: DragEvent) => void;
8
8
  on_drag_end?: () => void;
9
- type_mapper?: (filename: string) => string;
9
+ type_mapper?: (file: FileInfo) => string;
10
10
  file_type_colors?: Record<string, string>;
11
- [key: string]: unknown;
12
11
  }
13
12
  declare const FilePicker: import("svelte").Component<Props, {}, "">;
14
13
  type FilePicker = ReturnType<typeof FilePicker>;
package/dist/Icon.svelte CHANGED
@@ -9,7 +9,7 @@ const { path, ...svg_props } = $derived.by(() => {
9
9
  });
10
10
  </script>
11
11
 
12
- <svg fill="currentColor" {...svg_props} {...rest}>
12
+ <svg role="img" fill="currentColor" {...svg_props} {...rest}>
13
13
  {#if path.trim().startsWith(`<`)}
14
14
  {@html path}
15
15
  {:else}
@@ -1,7 +1,7 @@
1
+ import type { SVGAttributes } from 'svelte/elements';
1
2
  import { type IconName } from './icons';
2
- interface Props {
3
+ interface Props extends SVGAttributes<SVGSVGElement> {
3
4
  icon: IconName;
4
- [key: string]: unknown;
5
5
  }
6
6
  declare const Icon: import("svelte").Component<Props, {}, "">;
7
7
  type Icon = ReturnType<typeof Icon>;
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">import { format_num } from './';
2
- let { data = [], title = ``, fallback = ``, fmt = `.2f`, as = `section`, style = null, title_snippet, fallback_snippet, ...rest } = $props();
2
+ let { data = [], title = ``, fallback = ``, fmt = `.2f`, as = `section`, title_snippet, fallback_snippet, ...rest } = $props();
3
3
  // rename fmt as default_fmt internally
4
4
  let default_fmt = $derived(fmt);
5
5
  </script>
6
6
 
7
- <svelte:element this={as} class="info-card" {style} {...rest}>
7
+ <svelte:element this={as} {...rest} class="info-card {rest.class ?? ``}">
8
8
  {#if title || title_snippet}
9
9
  <h2>
10
10
  {#if title_snippet}{@render title_snippet()}{:else}
@@ -13,8 +13,8 @@ let default_fmt = $derived(fmt);
13
13
  </h2>
14
14
  {/if}
15
15
  {#each data.filter((itm) =>
16
- (!(`condition` in itm) || itm?.condition) &&
17
- ![undefined, null].includes(itm.value)
16
+ (!(`condition` in itm) || itm?.condition) && itm.value !== undefined &&
17
+ itm.value !== null
18
18
  ) as
19
19
  { title, value, unit, fmt = default_fmt, tooltip }
20
20
  (title + value + unit + fmt)
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
- interface Props {
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ interface Props<T extends keyof HTMLElementTagNameMap = `section`> extends HTMLAttributes<HTMLElementTagNameMap[T]> {
3
4
  data?: {
4
5
  title: string;
5
6
  value?: string | number | number[] | null;
@@ -11,12 +12,10 @@ interface Props {
11
12
  title?: string;
12
13
  fallback?: string;
13
14
  fmt?: string;
14
- as?: string;
15
- style?: string | null;
15
+ as?: T;
16
16
  title_snippet?: Snippet;
17
17
  fallback_snippet?: Snippet;
18
- [key: string]: unknown;
19
18
  }
20
- declare const InfoCard: import("svelte").Component<Props, {}, "">;
19
+ declare const InfoCard: import("svelte").Component<Props<"section">, {}, "">;
21
20
  type InfoCard = ReturnType<typeof InfoCard>;
22
21
  export default InfoCard;
@@ -59,7 +59,7 @@ function handle_reset(event) {
59
59
  }
60
60
  </script>
61
61
 
62
- <h4>
62
+ <h4 id="settings-section-title">
63
63
  {title}
64
64
 
65
65
  {#if has_changes}
@@ -74,7 +74,7 @@ function handle_reset(event) {
74
74
  </button>
75
75
  {/if}
76
76
  </h4>
77
- <section {...rest}>
77
+ <section {...rest} aria-labelledby="settings-section-title">
78
78
  {@render children()}
79
79
  </section>
80
80
 
@@ -1,10 +1,10 @@
1
1
  import type { Snippet } from 'svelte';
2
- interface Props {
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ interface Props extends HTMLAttributes<HTMLElementTagNameMap[`section`]> {
3
4
  title: string;
4
5
  current_values: Record<string, unknown>;
5
6
  children: Snippet<[]>;
6
7
  on_reset?: () => void;
7
- [key: string]: unknown;
8
8
  }
9
9
  declare const SettingsSection: import("svelte").Component<Props, {}, "">;
10
10
  type SettingsSection = ReturnType<typeof SettingsSection>;
@@ -1,8 +1,8 @@
1
- <script lang="ts">"use strict";
2
- let { text = `Loading...`, ...rest } = $props();
1
+ <script lang="ts">let { text = `Loading...`, ...rest } = $props();
2
+ export {};
3
3
  </script>
4
4
 
5
- <div class="spinner-overlay" role="status" aria-live="polite" {...rest}>
5
+ <div class="spinner-overlay" role="status" aria-live="polite" aria-busy="true" {...rest}>
6
6
  <div class="spinner" aria-hidden="true"></div>
7
7
  <p>{text}</p>
8
8
  </div>
@@ -1,6 +1,6 @@
1
- interface Props {
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
2
3
  text?: string;
3
- [key: string]: unknown;
4
4
  }
5
5
  declare const Spinner: import("svelte").Component<Props, {}, "">;
6
6
  type Spinner = ReturnType<typeof Spinner>;
@@ -10,13 +10,13 @@ export interface OptimadeStructure {
10
10
  lattice_vectors?: number[][];
11
11
  cartesian_site_positions?: number[][];
12
12
  species_at_sites?: string[];
13
- species?: Array<{
13
+ species?: {
14
14
  name: string;
15
15
  chemical_symbols?: string[];
16
16
  concentration?: number[];
17
17
  mass?: number[];
18
18
  original_name?: string;
19
- }>;
19
+ }[];
20
20
  n_sites?: number;
21
21
  last_modified?: string;
22
22
  immutable_id?: string;
package/dist/app.css CHANGED
@@ -4,9 +4,6 @@
4
4
  --page-bg: #090019;
5
5
  --max-text-width: 50em;
6
6
 
7
- --github-corner-color: var(--page-bg);
8
- --github-corner-bg: var(--text-color);
9
-
10
7
  --sms-max-width: 20em;
11
8
  --sms-text-color: var(--text-color);
12
9
  }
@@ -45,8 +42,7 @@ a {
45
42
  text-decoration: none;
46
43
  }
47
44
 
48
- button,
49
- a.btn {
45
+ button, a.btn {
50
46
  color: var(--text-color);
51
47
  cursor: pointer;
52
48
  border: none;
@@ -56,8 +52,7 @@ a.btn {
56
52
  a:hover {
57
53
  color: var(--accent-hover-color, orange);
58
54
  }
59
- code,
60
- kbd {
55
+ code, kbd {
61
56
  overflow-wrap: break-word;
62
57
  padding: 1pt 3pt;
63
58
  border-radius: 3pt;
@@ -155,7 +150,7 @@ table.roomy :is(td, th) {
155
150
  padding: 5pt 9pt;
156
151
  }
157
152
  tbody tr:nth-child(odd) {
158
- background: var(--surface-bg, black);
153
+ background: color-mix(in hsl, var(--page-bg, black), var(--text-color) 2%);
159
154
  }
160
155
 
161
156
  :where(ul.selected > li button, button.remove-all) {
@@ -174,3 +169,11 @@ tbody tr:nth-child(odd) {
174
169
  margin-left: calc(-50vw + 50% + max(var(--margin), (100vw - 1400px) / 2));
175
170
  max-width: none !important;
176
171
  }
172
+
173
+ .copy-btn {
174
+ position: absolute;
175
+ top: 9pt;
176
+ right: 9pt;
177
+ background: var(--btn-bg);
178
+ color: var(--btn-color);
179
+ }
@@ -3,6 +3,8 @@ import type { elem_symbols } from '../labels';
3
3
  export type D3InterpolateName = keyof typeof d3_sc & `interpolate${string}`;
4
4
  export type D3ColorSchemeName = D3InterpolateName extends `interpolate${infer Name}` ? Name : never;
5
5
  export declare const default_category_colors: Record<string, string>;
6
+ export declare const axis_colors: readonly [readonly ["x", "#d75555", "#e66666"], readonly ["y", "#55b855", "#66c966"], readonly ["z", "#5555d7", "#6666e6"]];
7
+ export declare const neg_axis_colors: readonly [readonly ["nx", "#b84444", "#cc5555"], readonly ["ny", "#44a044", "#55b155"], readonly ["nz", "#4444b8", "#5555c9"]];
6
8
  export type RGBColor = [number, number, number];
7
9
  export type ElementColorScheme = Record<(typeof elem_symbols)[number], RGBColor>;
8
10
  export declare const vesta_hex: Record<string, string>;
@@ -27,4 +29,10 @@ export declare const is_color: (val: unknown) => val is string;
27
29
  export declare const plot_colors: readonly ["#63b3ed", "#68d391", "#fbd38d", "#fc8181", "#d6bcfa", "#4fd1c7", "#f687b3", "#fed7d7", "#bee3f8", "#c6f6d5"];
28
30
  export declare function luminance(clr: string): number;
29
31
  export declare function get_bg_color(elem: HTMLElement | null, bg_color?: string | null): string;
30
- export declare function pick_color_for_contrast(node: HTMLElement | null, bg_color?: string | null, text_color_threshold?: number, choices?: [string, string]): string;
32
+ export interface ContrastOptions {
33
+ bg_color?: string;
34
+ text_color_threshold?: number;
35
+ choices?: [string, string];
36
+ }
37
+ export declare function pick_contrast_color(options?: ContrastOptions): string;
38
+ export declare const contrast_color: (options?: ContrastOptions) => (node: HTMLElement) => void;