matterviz 0.3.0 → 0.3.1
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.
- package/dist/MillerIndexInput.svelte +60 -0
- package/dist/MillerIndexInput.svelte.d.ts +7 -0
- package/dist/app.css +9 -2
- package/dist/brillouin/BrillouinZone.svelte +1 -1
- package/dist/composition/FormulaFilter.svelte +198 -10
- package/dist/composition/FormulaFilter.svelte.d.ts +2 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/convex-hull/ConvexHull.svelte +12 -1
- package/dist/convex-hull/ConvexHull2D.svelte +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +381 -128
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +76 -35
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +94 -31
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullStats.svelte +187 -93
- package/dist/convex-hull/GasPressureControls.svelte +72 -38
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
- package/dist/convex-hull/TemperatureSlider.svelte +46 -19
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
- package/dist/convex-hull/gas-thermodynamics.js +16 -5
- package/dist/convex-hull/helpers.d.ts +6 -0
- package/dist/convex-hull/helpers.js +43 -11
- package/dist/convex-hull/index.d.ts +14 -1
- package/dist/convex-hull/types.d.ts +2 -0
- package/dist/convex-hull/types.js +6 -0
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -0
- package/dist/fermi-surface/FermiSurface.svelte +1 -1
- package/dist/fermi-surface/compute.js +1 -21
- package/dist/fermi-surface/marching-cubes.d.ts +2 -13
- package/dist/fermi-surface/marching-cubes.js +2 -519
- package/dist/fermi-surface/parse.js +1 -1
- package/dist/icons.d.ts +8 -0
- package/dist/icons.js +8 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/isosurface/Isosurface.svelte +175 -0
- package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
- package/dist/isosurface/IsosurfaceControls.svelte +254 -0
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
- package/dist/isosurface/index.d.ts +5 -0
- package/dist/isosurface/index.js +6 -0
- package/dist/isosurface/parse.d.ts +6 -0
- package/dist/isosurface/parse.js +505 -0
- package/dist/isosurface/slice.d.ts +10 -0
- package/dist/isosurface/slice.js +145 -0
- package/dist/isosurface/types.d.ts +43 -0
- package/dist/isosurface/types.js +80 -0
- package/dist/layout/json-tree/JsonNode.svelte +196 -21
- package/dist/layout/json-tree/JsonTree.svelte +406 -33
- package/dist/layout/json-tree/JsonValue.svelte +44 -3
- package/dist/layout/json-tree/types.d.ts +19 -2
- package/dist/layout/json-tree/utils.d.ts +12 -1
- package/dist/layout/json-tree/utils.js +221 -0
- package/dist/marching-cubes.d.ts +14 -0
- package/dist/marching-cubes.js +519 -0
- package/dist/math.d.ts +1 -0
- package/dist/math.js +16 -0
- package/dist/overlays/DraggablePane.svelte +1 -1
- package/dist/plot/ColorBar.svelte +2 -2
- package/dist/plot/ColorScaleSelect.svelte +1 -1
- package/dist/plot/ElementScatter.svelte +1 -1
- package/dist/plot/data-cleaning.js +1 -5
- package/dist/plot/interactions.js +8 -16
- package/dist/plot/utils/label-placement.js +1 -1
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/index.d.ts +1 -1
- package/dist/rdf/index.js +1 -1
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +20 -0
- package/dist/structure/AtomLegend.svelte +2 -2
- package/dist/structure/Lattice.svelte +2 -0
- package/dist/structure/Structure.svelte +613 -60
- package/dist/structure/Structure.svelte.d.ts +7 -2
- package/dist/structure/StructureControls.svelte +10 -1
- package/dist/structure/StructureControls.svelte.d.ts +5 -1
- package/dist/structure/StructureScene.svelte +295 -42
- package/dist/structure/StructureScene.svelte.d.ts +14 -4
- package/dist/structure/index.d.ts +1 -0
- package/dist/table/HeatmapTable.svelte +45 -6
- package/dist/table/HeatmapTable.svelte.d.ts +2 -1
- package/dist/table/ToggleMenu.svelte +289 -44
- package/dist/table/ToggleMenu.svelte.d.ts +4 -1
- package/dist/table/index.d.ts +1 -0
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tooltip/index.js +1 -0
- package/package.json +15 -11
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">let { value = $bindable([0, 0, 1]) } = $props();
|
|
2
|
+
// Format: compact "001" for single-digit, spaced "10 0 1" for multi-digit
|
|
3
|
+
let hkl_text = $derived(value.every((v) => Math.abs(v) < 10) ? value.join(``) : value.join(` `));
|
|
4
|
+
// Parse hkl string: supports compact "001"/"-101" and spaced/comma "10, 0, 1"
|
|
5
|
+
function parse_hkl(input) {
|
|
6
|
+
// Try spaced/comma format first (handles multi-digit)
|
|
7
|
+
const spaced = input.trim().split(/[,\s]+/);
|
|
8
|
+
if (spaced.length === 3) {
|
|
9
|
+
const nums = spaced.map(Number);
|
|
10
|
+
if (nums.every((n) => !isNaN(n)))
|
|
11
|
+
return nums;
|
|
12
|
+
}
|
|
13
|
+
// Fall back to compact single-digit format: "001", "-101"
|
|
14
|
+
const compact = input.replace(/\s+/g, ``);
|
|
15
|
+
const match = compact.match(/^(-?\d)(-?\d)(-?\d)$/);
|
|
16
|
+
if (match)
|
|
17
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function handle_input(event) {
|
|
21
|
+
const parsed = parse_hkl(event.target.value);
|
|
22
|
+
if (parsed)
|
|
23
|
+
value = parsed;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<label class="miller-input">
|
|
29
|
+
<span>hkl</span>
|
|
30
|
+
<input
|
|
31
|
+
type="text"
|
|
32
|
+
value={hkl_text}
|
|
33
|
+
oninput={handle_input}
|
|
34
|
+
placeholder="001"
|
|
35
|
+
maxlength="12"
|
|
36
|
+
title="Miller indices (e.g. 001, -101, or 10 0 1)"
|
|
37
|
+
/>
|
|
38
|
+
</label>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.miller-input {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 0.3em;
|
|
45
|
+
span {
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
font-size: 0.85em;
|
|
48
|
+
}
|
|
49
|
+
input {
|
|
50
|
+
width: 4em;
|
|
51
|
+
padding: 0.15em 0.3em;
|
|
52
|
+
border: 1px solid var(--border-color, #ccc);
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
font-family: monospace;
|
|
55
|
+
font-size: 0.9em;
|
|
56
|
+
text-align: center;
|
|
57
|
+
box-sizing: border-box;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Vec3 } from './math';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
value?: Vec3;
|
|
4
|
+
};
|
|
5
|
+
declare const MillerIndexInput: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
6
|
+
type MillerIndexInput = ReturnType<typeof MillerIndexInput>;
|
|
7
|
+
export default MillerIndexInput;
|
package/dist/app.css
CHANGED
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
--max-text-width: 50em;
|
|
9
9
|
|
|
10
10
|
--sms-max-width: 20em;
|
|
11
|
+
--sms-min-height: 19pt;
|
|
11
12
|
--sms-text-color: var(--text-color);
|
|
12
13
|
|
|
13
14
|
/* Svelte MultiSelect */
|
|
14
15
|
--sms-options-bg: var(--page-bg);
|
|
15
16
|
--sms-active-color: light-dark(var(--accent-color), cornflowerblue);
|
|
16
17
|
--sms-li-active-bg: light-dark(rgba(100, 149, 237, 0.25), cornflowerblue);
|
|
18
|
+
--sms-selected-bg: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.15));
|
|
17
19
|
--border-radius: 3pt;
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -33,18 +35,23 @@
|
|
|
33
35
|
}
|
|
34
36
|
body {
|
|
35
37
|
background: var(--page-bg);
|
|
36
|
-
padding:
|
|
38
|
+
padding: 5vh 3vw 3vh;
|
|
37
39
|
font-family: -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
|
|
38
40
|
margin: auto;
|
|
39
41
|
color: var(--text-color);
|
|
40
42
|
line-height: 1.5;
|
|
43
|
+
/* Sticky footer: footer stays at bottom when content is short */
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
min-height: 100vh;
|
|
47
|
+
box-sizing: border-box;
|
|
41
48
|
}
|
|
42
49
|
main {
|
|
43
50
|
margin: auto;
|
|
44
|
-
margin-bottom: 3em;
|
|
45
51
|
width: 100%;
|
|
46
52
|
max-width: var(--max-text-width);
|
|
47
53
|
container-type: inline-size;
|
|
54
|
+
flex: 1; /* Grow to fill available space (sticky footer) */
|
|
48
55
|
}
|
|
49
56
|
a {
|
|
50
57
|
color: var(--accent-color, cornflowerblue);
|
|
@@ -394,7 +394,7 @@ $effect(() => {
|
|
|
394
394
|
display: flex;
|
|
395
395
|
padding: 4px;
|
|
396
396
|
border-radius: var(--border-radius, 3pt);
|
|
397
|
-
font-size: clamp(0.85em, 2cqmin,
|
|
397
|
+
font-size: clamp(0.85em, 2cqmin, 1.3em);
|
|
398
398
|
}
|
|
399
399
|
section.control-buttons :global(button:hover) {
|
|
400
400
|
background-color: color-mix(in srgb, currentColor 8%, transparent);
|
|
@@ -3,7 +3,7 @@ import { tooltip } from 'svelte-multiselect';
|
|
|
3
3
|
import { extract_formula_elements, has_wildcards, normalize_element_symbols, parse_formula_with_wildcards, } from './parse';
|
|
4
4
|
const SEARCH_EXAMPLES = [
|
|
5
5
|
{
|
|
6
|
-
label: `
|
|
6
|
+
label: `Has elements`,
|
|
7
7
|
description: `Materials containing at least these elements (may have others). Use * for any element.`,
|
|
8
8
|
examples: [`Li,Fe`, `Si,O`, `Li,*,*`],
|
|
9
9
|
},
|
|
@@ -18,23 +18,90 @@ const SEARCH_EXAMPLES = [
|
|
|
18
18
|
examples: [`LiFePO4`, `LiFe*2*`, `*2O3`],
|
|
19
19
|
},
|
|
20
20
|
];
|
|
21
|
-
let { value = $bindable(``), search_mode = $bindable(`elements`), input_element = $bindable(null), show_clear_button = true, show_examples = true, disabled = false,
|
|
21
|
+
let { value = $bindable(``), search_mode = $bindable(`elements`), input_element = $bindable(null), show_clear_button = true, show_examples = true, disabled = false, max_history = 5, // Max recent inputs to remember; 0 disables history dropdown
|
|
22
|
+
history_key = `formula-filter-history`, // localStorage key for persisting history
|
|
23
|
+
onchange, onclear, ...rest } = $props();
|
|
22
24
|
let input_value = $state(value);
|
|
23
25
|
let examples_open = $state(false);
|
|
26
|
+
let history_open = $state(false);
|
|
24
27
|
let wrapper = $state(null);
|
|
25
28
|
let examples_wrapper = $state(null);
|
|
26
29
|
let focused_item_idx = $state(-1);
|
|
30
|
+
let focused_history_idx = $state(-1);
|
|
27
31
|
let anchor_left = $state(false);
|
|
28
32
|
// Flatten examples for keyboard navigation
|
|
29
33
|
const all_examples = SEARCH_EXAMPLES.flatMap((cat) => cat.examples);
|
|
34
|
+
// === History Management ===
|
|
35
|
+
const has_storage = typeof localStorage !== `undefined`;
|
|
36
|
+
function load_history() {
|
|
37
|
+
if (max_history <= 0 || !has_storage)
|
|
38
|
+
return [];
|
|
39
|
+
try {
|
|
40
|
+
const raw = localStorage.getItem(history_key);
|
|
41
|
+
if (!raw)
|
|
42
|
+
return [];
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
if (!Array.isArray(parsed))
|
|
45
|
+
return [];
|
|
46
|
+
return parsed.filter((item) => typeof item === `string`).slice(0, max_history);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function save_history(entries) {
|
|
53
|
+
if (max_history <= 0 || !has_storage)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
localStorage.setItem(history_key, JSON.stringify(entries.slice(0, max_history)));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// localStorage may be unavailable (e.g. private browsing)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let history = $state(load_history());
|
|
63
|
+
function add_to_history(entry) {
|
|
64
|
+
if (max_history <= 0 || !entry.trim())
|
|
65
|
+
return;
|
|
66
|
+
// Remove duplicate if present, then prepend
|
|
67
|
+
const filtered = history.filter((item) => item !== entry);
|
|
68
|
+
history = [entry, ...filtered].slice(0, max_history);
|
|
69
|
+
save_history(history);
|
|
70
|
+
}
|
|
71
|
+
function remove_from_history(entry) {
|
|
72
|
+
history = history.filter((item) => item !== entry);
|
|
73
|
+
save_history(history);
|
|
74
|
+
// Clamp focused index to prevent out-of-bounds access on Enter
|
|
75
|
+
if (history.length === 0)
|
|
76
|
+
history_open = false;
|
|
77
|
+
else if (focused_history_idx >= visible_history.length) {
|
|
78
|
+
focused_history_idx = visible_history.length - 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Filtered history: exclude current value to avoid redundant suggestion
|
|
82
|
+
let visible_history = $derived(history.filter((item) => item !== value));
|
|
83
|
+
function close_history() {
|
|
84
|
+
history_open = false;
|
|
85
|
+
focused_history_idx = -1;
|
|
86
|
+
}
|
|
87
|
+
function open_history() {
|
|
88
|
+
if (max_history <= 0 || visible_history.length === 0 || examples_open)
|
|
89
|
+
return;
|
|
90
|
+
history_open = true;
|
|
91
|
+
focused_history_idx = -1;
|
|
92
|
+
}
|
|
30
93
|
function handle_document_click(event) {
|
|
31
|
-
if (!wrapper || !examples_open)
|
|
94
|
+
if (!wrapper || (!examples_open && !history_open))
|
|
32
95
|
return;
|
|
33
96
|
const target = event.target;
|
|
34
97
|
if (!(target instanceof Node))
|
|
35
98
|
return;
|
|
36
|
-
if (!wrapper.contains(target))
|
|
37
|
-
|
|
99
|
+
if (!wrapper.contains(target)) {
|
|
100
|
+
if (examples_open)
|
|
101
|
+
close_examples();
|
|
102
|
+
if (history_open)
|
|
103
|
+
close_history();
|
|
104
|
+
}
|
|
38
105
|
}
|
|
39
106
|
function close_examples(restore_focus = true) {
|
|
40
107
|
examples_open = false;
|
|
@@ -75,7 +142,7 @@ function infer_mode(input) {
|
|
|
75
142
|
if (!trimmed)
|
|
76
143
|
return `elements`;
|
|
77
144
|
if (trimmed.includes(`,`))
|
|
78
|
-
return `elements`; // Li,Fe,O →
|
|
145
|
+
return `elements`; // Li,Fe,O → has elements
|
|
79
146
|
if (trimmed.includes(`-`))
|
|
80
147
|
return `chemsys`; // Li-Fe-O → chemical system
|
|
81
148
|
return `exact`; // LiFePO4 → exact formula
|
|
@@ -141,6 +208,9 @@ function set_value(new_value) {
|
|
|
141
208
|
const mode = infer_mode(new_value);
|
|
142
209
|
last_synced = value = input_value = new_value; // update last_synced to prevent effect re-inference
|
|
143
210
|
search_mode = mode;
|
|
211
|
+
if (new_value.trim())
|
|
212
|
+
add_to_history(new_value);
|
|
213
|
+
close_history();
|
|
144
214
|
onchange?.(value, mode);
|
|
145
215
|
}
|
|
146
216
|
function sync_value() {
|
|
@@ -167,14 +237,34 @@ function sync_value() {
|
|
|
167
237
|
function onkeydown(event) {
|
|
168
238
|
if (event.key === `Enter`) {
|
|
169
239
|
event.preventDefault();
|
|
170
|
-
|
|
240
|
+
if (history_open && focused_history_idx >= 0) {
|
|
241
|
+
set_value(visible_history[focused_history_idx]);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
sync_value();
|
|
245
|
+
}
|
|
171
246
|
}
|
|
172
247
|
else if (event.key === `Escape`) {
|
|
173
|
-
if (
|
|
248
|
+
if (history_open)
|
|
249
|
+
close_history();
|
|
250
|
+
else if (examples_open)
|
|
174
251
|
examples_open = false;
|
|
175
252
|
else if (input_value)
|
|
176
253
|
clear_filter();
|
|
177
254
|
}
|
|
255
|
+
else if (history_open && visible_history.length > 0) {
|
|
256
|
+
const len = visible_history.length;
|
|
257
|
+
if (event.key === `ArrowDown`) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
focused_history_idx = (focused_history_idx + 1) % len;
|
|
260
|
+
}
|
|
261
|
+
else if (event.key === `ArrowUp`) {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
focused_history_idx = focused_history_idx <= 0
|
|
264
|
+
? len - 1
|
|
265
|
+
: focused_history_idx - 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
178
268
|
}
|
|
179
269
|
function clear_filter() {
|
|
180
270
|
onclear?.();
|
|
@@ -186,6 +276,7 @@ function apply_example(example) {
|
|
|
186
276
|
}
|
|
187
277
|
function toggle_examples(event) {
|
|
188
278
|
event.stopPropagation();
|
|
279
|
+
close_history();
|
|
189
280
|
examples_open = !examples_open;
|
|
190
281
|
focused_item_idx = examples_open ? 0 : -1;
|
|
191
282
|
if (examples_open)
|
|
@@ -224,7 +315,7 @@ let placeholder = $derived(search_mode === `chemsys`
|
|
|
224
315
|
? `LiFePO4 or LiFe*2*`
|
|
225
316
|
: `Li,Fe,O or Li,*,*`);
|
|
226
317
|
const MODE_LABELS = {
|
|
227
|
-
elements: `
|
|
318
|
+
elements: `has elements`,
|
|
228
319
|
chemsys: `chemical system`,
|
|
229
320
|
exact: `exact formula`,
|
|
230
321
|
};
|
|
@@ -244,12 +335,51 @@ let next_mode = $derived.by(() => {
|
|
|
244
335
|
<input
|
|
245
336
|
bind:this={input_element}
|
|
246
337
|
bind:value={input_value}
|
|
247
|
-
onblur={
|
|
338
|
+
onblur={() => {
|
|
339
|
+
// mousedown preventDefault on history items prevents blur, so this only
|
|
340
|
+
// fires when focus genuinely leaves (tab out, click outside, etc.)
|
|
341
|
+
// sync_value → set_value → close_history, so no separate close needed
|
|
342
|
+
sync_value()
|
|
343
|
+
}}
|
|
344
|
+
onfocus={open_history}
|
|
248
345
|
{onkeydown}
|
|
249
346
|
{placeholder}
|
|
250
347
|
{disabled}
|
|
251
348
|
aria-label="Formula filter"
|
|
252
349
|
/>
|
|
350
|
+
{#if history_open && visible_history.length > 0}
|
|
351
|
+
<div class="history-dropdown" role="listbox" aria-label="Recent searches">
|
|
352
|
+
<span class="history-header">Recent</span>
|
|
353
|
+
{#each visible_history as entry, idx (entry)}
|
|
354
|
+
<div class="history-item" class:focused={idx === focused_history_idx}>
|
|
355
|
+
<button
|
|
356
|
+
type="button"
|
|
357
|
+
class="history-value"
|
|
358
|
+
role="option"
|
|
359
|
+
aria-selected={idx === focused_history_idx}
|
|
360
|
+
onmousedown={(event) => {
|
|
361
|
+
event.preventDefault()
|
|
362
|
+
set_value(entry)
|
|
363
|
+
}}
|
|
364
|
+
>
|
|
365
|
+
{entry}
|
|
366
|
+
</button>
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
class="history-remove"
|
|
370
|
+
title="Remove from history"
|
|
371
|
+
aria-label="Remove {entry} from history"
|
|
372
|
+
onmousedown={(event) => {
|
|
373
|
+
event.preventDefault()
|
|
374
|
+
remove_from_history(entry)
|
|
375
|
+
}}
|
|
376
|
+
>
|
|
377
|
+
<Icon icon="Close" style="width: 0.7em; height: 0.7em" />
|
|
378
|
+
</button>
|
|
379
|
+
</div>
|
|
380
|
+
{/each}
|
|
381
|
+
</div>
|
|
382
|
+
{/if}
|
|
253
383
|
{#if input_value}
|
|
254
384
|
<button
|
|
255
385
|
type="button"
|
|
@@ -394,6 +524,64 @@ let next_mode = $derived.by(() => {
|
|
|
394
524
|
opacity: 1;
|
|
395
525
|
color: var(--highlight, #4db6ff);
|
|
396
526
|
}
|
|
527
|
+
.history-dropdown {
|
|
528
|
+
position: absolute;
|
|
529
|
+
top: calc(100% + 2pt);
|
|
530
|
+
left: 0;
|
|
531
|
+
right: 0;
|
|
532
|
+
z-index: 101;
|
|
533
|
+
background: var(--dropdown-bg, var(--surface-bg, #fff));
|
|
534
|
+
border: 1px solid var(--dropdown-border, rgba(128, 128, 128, 0.2));
|
|
535
|
+
border-radius: 8px;
|
|
536
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
537
|
+
padding: 4pt 0;
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-direction: column;
|
|
540
|
+
}
|
|
541
|
+
.history-header {
|
|
542
|
+
font-size: 0.7em;
|
|
543
|
+
font-weight: 600;
|
|
544
|
+
opacity: 0.45;
|
|
545
|
+
padding: 2pt 10pt 4pt;
|
|
546
|
+
text-transform: uppercase;
|
|
547
|
+
letter-spacing: 0.5px;
|
|
548
|
+
}
|
|
549
|
+
.history-item {
|
|
550
|
+
display: flex;
|
|
551
|
+
align-items: center;
|
|
552
|
+
padding: 0 4pt 0 0;
|
|
553
|
+
}
|
|
554
|
+
.history-item.focused,
|
|
555
|
+
.history-item:hover {
|
|
556
|
+
background: rgba(77, 182, 255, 0.08);
|
|
557
|
+
}
|
|
558
|
+
.history-value {
|
|
559
|
+
flex: 1;
|
|
560
|
+
text-align: left;
|
|
561
|
+
background: none;
|
|
562
|
+
border: none;
|
|
563
|
+
cursor: pointer;
|
|
564
|
+
padding: 4pt 10pt;
|
|
565
|
+
font-family: var(--mono-font, monospace);
|
|
566
|
+
font-size: 0.88em;
|
|
567
|
+
color: inherit;
|
|
568
|
+
}
|
|
569
|
+
.history-remove {
|
|
570
|
+
display: flex;
|
|
571
|
+
align-items: center;
|
|
572
|
+
justify-content: center;
|
|
573
|
+
background: none;
|
|
574
|
+
border: none;
|
|
575
|
+
cursor: pointer;
|
|
576
|
+
padding: 3pt;
|
|
577
|
+
border-radius: 50%;
|
|
578
|
+
opacity: 0.3;
|
|
579
|
+
color: inherit;
|
|
580
|
+
}
|
|
581
|
+
.history-remove:hover {
|
|
582
|
+
opacity: 0.8;
|
|
583
|
+
background: rgba(128, 128, 128, 0.15);
|
|
584
|
+
}
|
|
397
585
|
.examples-wrapper {
|
|
398
586
|
position: relative;
|
|
399
587
|
}
|
|
@@ -7,6 +7,8 @@ type $$ComponentProps = {
|
|
|
7
7
|
show_clear_button?: boolean;
|
|
8
8
|
show_examples?: boolean;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
max_history?: number;
|
|
11
|
+
history_key?: string;
|
|
10
12
|
onchange?: (value: string, search_mode: FormulaSearchMode) => void;
|
|
11
13
|
onclear?: () => void;
|
|
12
14
|
} & HTMLAttributes<HTMLDivElement>;
|
package/dist/constants.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare const STRUCTURE_EXTENSIONS_REGEX: RegExp;
|
|
|
21
21
|
export declare const TRAJ_FALLBACK_EXTENSIONS: readonly string[];
|
|
22
22
|
export declare const TRAJ_FALLBACK_EXTENSIONS_REGEX: RegExp;
|
|
23
23
|
export declare const VASP_FILES_REGEX: RegExp;
|
|
24
|
+
export declare const VASP_VOLUMETRIC_REGEX: RegExp;
|
|
24
25
|
export declare const XDATCAR_REGEX: RegExp;
|
|
25
26
|
export declare const CONFIG_DIRS_REGEX: RegExp;
|
|
26
27
|
export declare const MD_SIM_EXCLUDE_REGEX: RegExp;
|
package/dist/constants.js
CHANGED
|
@@ -75,6 +75,7 @@ export const STRUCTURE_EXTENSIONS = Object.freeze([
|
|
|
75
75
|
`.mcif`,
|
|
76
76
|
`.poscar`,
|
|
77
77
|
`.vasp`,
|
|
78
|
+
`.cube`,
|
|
78
79
|
`.lmp`,
|
|
79
80
|
`.data`,
|
|
80
81
|
`.dump`,
|
|
@@ -95,6 +96,7 @@ export const TRAJ_FALLBACK_EXTENSIONS = Object.freeze([
|
|
|
95
96
|
export const TRAJ_FALLBACK_EXTENSIONS_REGEX = new RegExp(`\\.(${TRAJ_FALLBACK_EXTENSIONS.map((ext) => ext.slice(1)).join(`|`)})$`, `i`);
|
|
96
97
|
// Special regex patterns
|
|
97
98
|
export const VASP_FILES_REGEX = /(?:^|[\\/_.-])(poscar|contcar|potcar|incar|kpoints|outcar)(?:[\\/_.-]|$)/i;
|
|
99
|
+
export const VASP_VOLUMETRIC_REGEX = /(?:^|[\\/_.-])(chgcar|aeccar[012]?|elfcar|locpot|parchg)(?:[\\/_.-]|$)/i;
|
|
98
100
|
export const XDATCAR_REGEX = /xdatcar/i;
|
|
99
101
|
export const CONFIG_DIRS_REGEX = /(?:^|[\\/])(\.vscode|\.idea|\.nyc_output|\.cache|\.tmp|\.temp|node_modules|dist|build|coverage)(?:[\\/]|$)/i;
|
|
100
102
|
export const MD_SIM_EXCLUDE_REGEX = /md_simulation\.(out|txt|yml|py|csv|html|css|md|js|ts)$/i;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">import { extract_formula_elements } from '../composition/parse';
|
|
2
|
+
import { DEFAULTS } from '../settings';
|
|
2
3
|
import { SvelteSet } from 'svelte/reactivity';
|
|
3
4
|
import ConvexHull2D from './ConvexHull2D.svelte';
|
|
4
5
|
import ConvexHull3D from './ConvexHull3D.svelte';
|
|
5
6
|
import ConvexHull4D from './ConvexHull4D.svelte';
|
|
6
7
|
let { entries = [],
|
|
7
8
|
// bindable props not part of rest because Svelte 5 doesn't support spreading bindable props.
|
|
8
|
-
fullscreen = $bindable(false), wrapper = $bindable(), show_stable = $bindable(true), show_unstable = $bindable(true), show_hull_faces = $bindable(true), hull_face_opacity = $bindable(
|
|
9
|
+
fullscreen = $bindable(false), wrapper = $bindable(), show_stable = $bindable(true), show_unstable = $bindable(true), show_hull_faces = $bindable(true), hull_face_opacity: hull_face_opacity_prop = $bindable(undefined), color_mode = $bindable(`energy`), color_scale = $bindable(`interpolateViridis`), info_pane_open = $bindable(false), legend_pane_open = $bindable(false), max_hull_dist_show_phases = $bindable(0.1), max_hull_dist_show_labels = $bindable(0.1), show_stable_labels = $bindable(true), show_unstable_labels = $bindable(false), energy_source_mode = $bindable(`precomputed`), phase_stats = $bindable(null), display = $bindable({ x_grid: false, y_grid: false }), stable_entries = $bindable([]), unstable_entries = $bindable([]), highlighted_entries = $bindable([]), selected_entry = $bindable(null), temperature = $bindable(), gas_pressures = $bindable({}), ...rest } = $props();
|
|
9
10
|
// Lightweight element extraction - count unique elements, stripping oxidation states
|
|
10
11
|
// (e.g. "V4+" -> "V") to avoid counting the same element multiple times
|
|
11
12
|
function extract_unique_elements(entries) {
|
|
@@ -23,6 +24,16 @@ function extract_unique_elements(entries) {
|
|
|
23
24
|
// Detect dimensionality by counting unique elements (lightweight operation)
|
|
24
25
|
const elements = $derived(extract_unique_elements(entries));
|
|
25
26
|
const element_count = $derived(elements.length);
|
|
27
|
+
// Resolve hull face opacity: use caller's value if provided,
|
|
28
|
+
// otherwise pick the right default for the dimensionality (ternary=30%, quaternary=3%)
|
|
29
|
+
// Writable $derived handles forward sync (prop→local), back-sync $effect handles local→prop
|
|
30
|
+
const default_opacity = $derived(element_count === 4
|
|
31
|
+
? DEFAULTS.convex_hull.quaternary.hull_face_opacity
|
|
32
|
+
: DEFAULTS.convex_hull.ternary.hull_face_opacity);
|
|
33
|
+
let hull_face_opacity = $derived(hull_face_opacity_prop ?? default_opacity);
|
|
34
|
+
$effect(() => {
|
|
35
|
+
hull_face_opacity_prop = hull_face_opacity;
|
|
36
|
+
});
|
|
26
37
|
// Map element count to corresponding component
|
|
27
38
|
// Note: Type assertion needed because TypeScript can't infer that all components
|
|
28
39
|
// accept a compatible superset of props (BaseConvexHullProps + dimension-specific)
|
|
@@ -647,7 +647,7 @@ let style = $derived(`--hull-stable-color:${merged_config.colors?.stable || `#00
|
|
|
647
647
|
color: var(--text-color, currentColor);
|
|
648
648
|
transition: background-color 0.2s, opacity 0.2s;
|
|
649
649
|
display: flex;
|
|
650
|
-
font-size: clamp(0.85em, 2cqmin,
|
|
650
|
+
font-size: clamp(0.85em, 2cqmin, 1.3em);
|
|
651
651
|
}
|
|
652
652
|
:global(.convex-hull-2d .control-btn:hover) {
|
|
653
653
|
background-color: color-mix(in srgb, currentColor 8%, transparent);
|