Skip to content

Commit f449868

Browse files
Update modes
1 parent c2f6464 commit f449868

5 files changed

Lines changed: 1025 additions & 890 deletions

File tree

app.js

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,13 +1008,27 @@
10081008
}
10091009
window.renderStoryTier = renderStoryTier;
10101010

1011-
function setActiveTier(tier) {
1011+
function updateTierInUrl(tier) {
1012+
if (!window.history || typeof window.history.replaceState !== "function") {
1013+
return;
1014+
}
1015+
const params = new URLSearchParams(window.location.search);
1016+
params.set("tier", tier);
1017+
const query = params.toString();
1018+
window.history.replaceState({}, "", window.location.pathname + (query ? "?" + query : ""));
1019+
}
1020+
1021+
function setActiveTier(tier, options = {}) {
10121022
const validTiers = ["story", "explorer", "observatory"];
10131023
if (!validTiers.includes(tier)) return;
10141024

10151025
document.body.dataset.tier = tier;
10161026
const tierButtons = Array.from(document.querySelectorAll(".tier-button"));
1017-
tierButtons.forEach((btn) => btn.classList.toggle("is-active", btn.dataset.tier === tier));
1027+
tierButtons.forEach((btn) => {
1028+
const isActive = btn.dataset.tier === tier;
1029+
btn.classList.toggle("is-active", isActive);
1030+
btn.setAttribute("aria-selected", isActive ? "true" : "false");
1031+
});
10181032

10191033
const observatorySubtabs = document.getElementById("observatory-subtabs");
10201034
if (observatorySubtabs) {
@@ -1038,8 +1052,26 @@ function setActiveTier(tier) {
10381052
applyShellMode("observatory", { updateUrl: false });
10391053
}
10401054
}
1055+
1056+
if (options.updateUrl !== false) {
1057+
updateTierInUrl(tier);
1058+
}
1059+
}
1060+
1061+
window.setActiveTier = setActiveTier;
1062+
1063+
function applyTierFromUrl() {
1064+
const params = new URLSearchParams(window.location.search);
1065+
const tier = params.get("tier");
1066+
if (tier) {
1067+
setActiveTier(tier, { updateUrl: false });
1068+
return true;
1069+
}
1070+
return false;
10411071
}
10421072

1073+
window.applyTierFromUrl = applyTierFromUrl;
1074+
10431075
async function initializePolyglotStudioVisualization(entityId) {
10441076
try {
10451077
const container = document.getElementById("polyglot-studio-visualization");
@@ -4405,6 +4437,9 @@ function setActiveTier(tier) {
44054437
return false;
44064438
}
44074439
await applyShellMode(mode, { updateUrl: false });
4440+
if (!params.get("tier")) {
4441+
setActiveTier(mode === "recit" ? "story" : "observatory", { updateUrl: false });
4442+
}
44084443
return true;
44094444
}
44104445

@@ -4546,35 +4581,61 @@ function setActiveTier(tier) {
45464581

45474582
// Search mode toggle in Explorer panel
45484583
let currentSearchMode = "entity";
4584+
window.getActiveSearchMode = function getActiveSearchMode() {
4585+
return currentSearchMode;
4586+
};
4587+
4588+
function updateSearchModeSurface(mode) {
4589+
document.querySelectorAll(".search-mode-btn").forEach((button) => {
4590+
const isActive = button.dataset.searchMode === mode;
4591+
button.classList.toggle("is-active", isActive);
4592+
button.setAttribute("aria-pressed", isActive ? "true" : "false");
4593+
});
4594+
const hintEl = document.getElementById("search-mode-hint");
4595+
const searchInput = document.getElementById("search-input");
4596+
const hints = {
4597+
entity: "Search for any movement, artist, artwork, museum, or theme.",
4598+
subject: "Enter a subject such as dog, cat, flower, sea, or portrait to find artworks depicting it.",
4599+
nationality: "Enter a country to discover artists from that place. This guided query is staged next.",
4600+
material: "Enter a material or technique such as oil, marble, bronze, or watercolor. This guided query is staged next."
4601+
};
4602+
const placeholders = {
4603+
entity: "Search movements, artists, works...",
4604+
subject: "Artworks depicting dog, cat, flower...",
4605+
nationality: "Artists from France, Japan, Mexico...",
4606+
material: "Works made with oil, marble, bronze..."
4607+
};
4608+
if (hintEl) {
4609+
hintEl.textContent = hints[mode] || hints.entity;
4610+
}
4611+
if (searchInput) {
4612+
searchInput.placeholder = placeholders[mode] || placeholders.entity;
4613+
}
4614+
}
4615+
45494616
document.addEventListener("click", (e) => {
45504617
const btn = e.target.closest(".search-mode-btn");
45514618
if (!btn) return;
45524619
const mode = btn.dataset.searchMode;
45534620
if (!mode) return;
45544621
currentSearchMode = mode;
4555-
document.querySelectorAll(".search-mode-btn").forEach((b) => {
4556-
b.classList.toggle("is-active", b.dataset.searchMode === mode);
4557-
});
4558-
const hintEl = document.getElementById("search-mode-hint");
4559-
const hints = {
4560-
entity: "Search for any movement, artist, or artwork",
4561-
"artworks-of": "Enter an artist name to see their artworks",
4562-
"artists-from": "Enter a country to discover its artists",
4563-
"by-material": "Enter a material (oil, marble, bronze…)"
4564-
};
4565-
if (hintEl) hintEl.textContent = hints[mode] || "";
4622+
updateSearchModeSurface(mode);
45664623
});
45674624

45684625
window.searchEntities = async function searchEntities(query) {
45694626
if (!query || query.length < 2) return [];
45704627
const lang = runtimeState.currentLanguage || "fr";
4628+
const mode = typeof window.getActiveSearchMode === "function" ? window.getActiveSearchMode() : currentSearchMode;
45714629
const queryLower = query.toLowerCase();
45724630
const seen = new Set();
45734631
const results = [];
4574-
for (const node of browserAdapterState.graphe.noeuds.values()) {
4575-
if (node.etiquette && node.etiquette.toLowerCase().includes(queryLower)) {
4576-
seen.add(node.id);
4577-
results.push({ id: node.id, label: node.etiquette, description: "(dans le graphe chargé)", entityType: node.type || "" });
4632+
4633+
if (mode === "entity") {
4634+
for (const node of browserAdapterState.graphe.noeuds.values()) {
4635+
if (node.etiquette && node.etiquette.toLowerCase().includes(queryLower)) {
4636+
seen.add(node.id);
4637+
results.push({ id: node.id, label: node.etiquette, description: "(dans le graphe chargé)", entityType: node.type || "", primaryAction: "Open" });
4638+
}
45784639
}
45794640
}
45804641
try {
@@ -4585,7 +4646,21 @@ function setActiveTier(tier) {
45854646
const id = item.id || "";
45864647
if (id && !seen.has(id)) {
45874648
seen.add(id);
4588-
results.push({ id, label: item.label || id, description: item.description || "", entityType: "" });
4649+
let entityType = "";
4650+
let primaryAction = "Open";
4651+
let description = item.description || "";
4652+
if (mode === "subject") {
4653+
entityType = "subject";
4654+
primaryAction = "Find artworks";
4655+
description = description ? description + " · subject search" : "Subject search";
4656+
} else if (mode === "nationality") {
4657+
primaryAction = "Use as country";
4658+
description = description ? description + " · artists-by-place is next" : "Artists-by-place is next";
4659+
} else if (mode === "material") {
4660+
primaryAction = "Use as material";
4661+
description = description ? description + " · material query is next" : "Material query is next";
4662+
}
4663+
results.push({ id, label: item.label || id, description, entityType, searchMode: mode, primaryAction });
45894664
}
45904665
}
45914666
} catch (e) {
@@ -4597,5 +4672,7 @@ function setActiveTier(tier) {
45974672
buildShellFilter();
45984673
loadQueryInventory();
45994674
updateLanguageSurface("fr");
4675+
updateSearchModeSurface(currentSearchMode);
46004676
// Render Story tier content on load (body starts as data-tier="story")
46014677
renderStoryTier();
4678+
applyTierFromUrl();

bootstrap.js

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,41 @@
1515

1616
window.renderComparisonPanel = function() {
1717
const container = document.getElementById("comparison-panel-container");
18-
const root = document.getElementById("__ml_comparison_root");
19-
if (!container || !root) return;
18+
const roots = [
19+
document.getElementById("__ml_comparison_root"),
20+
document.getElementById("__ml_explorer_comparison_root")
21+
].filter(Boolean);
22+
if (!roots.length) return;
2023
try {
2124
const render = window.ui?.composants?.panneau_comparaison?.rendre_panneau_comparaison;
2225
const html = typeof render === "function" ? render() || "" : "";
23-
root.innerHTML = html;
24-
container.hidden = !html;
26+
roots.forEach((root) => {
27+
root.innerHTML = html;
28+
});
29+
if (container) {
30+
container.hidden = !html;
31+
}
2532
} catch (e) {
2633
console.warn("comparison panel render error:", e);
2734
}
2835
};
2936

3037
window.renderTrajectoirePanel = function() {
3138
const container = document.getElementById("trajectoire-panel-container");
32-
const root = document.getElementById("__ml_trajectoire_root");
33-
if (!container || !root) return;
39+
const roots = [
40+
document.getElementById("__ml_trajectoire_root"),
41+
document.getElementById("__ml_explorer_trajectoire_root")
42+
].filter(Boolean);
43+
if (!roots.length) return;
3444
try {
3545
const render = window.ui?.composants?.trajectoire?.rendre_trajectoire;
3646
const html = typeof render === "function" ? render() || "" : "";
37-
root.innerHTML = html;
38-
container.hidden = !html;
47+
roots.forEach((root) => {
48+
root.innerHTML = html;
49+
});
50+
if (container) {
51+
container.hidden = !html;
52+
}
3953
} catch (e) {
4054
console.warn("trajectoire panel render error:", e);
4155
}
@@ -289,12 +303,15 @@
289303
if (results.length > 0) {
290304
const markup = results.slice(0, 8).map(item => {
291305
const entityType = item.entityType || inferSearchResultType(item);
306+
const mode = item.searchMode || "";
307+
const primaryAction = item.primaryAction || "Open";
292308
const trajBtn = (entityType === "artist" || entityType === "artiste")
293309
? `<button class="trajectoire-btn" data-action="trajectoire" data-traj-id="${item.id}" data-traj-label="${(item.label || item.id).replace(/"/g, '&quot;')}" aria-label="Trajectoire ${item.label || item.id}">+ Trajectoire</button>`
294310
: "";
295-
return `<div class="search-result-item" data-entity-id="${item.id}" data-entity-type="${entityType}">
311+
return `<div class="search-result-item" data-entity-id="${item.id}" data-entity-type="${entityType}" data-search-mode="${mode}">
296312
<strong>${item.label || item.id}</strong>
297313
<small>${item.description || entityType}</small>
314+
<span class="search-primary-action">${primaryAction}</span>
298315
<button class="compare-btn" data-action="comparer" data-compare-id="${item.id}" data-compare-type="${entityType}" aria-label="Comparer ${item.label || item.id}">+ Comparer</button>
299316
${trajBtn}
300317
</div>`
@@ -338,7 +355,13 @@
338355
const entityId = this.getAttribute("data-entity-id");
339356
const hintedType = this.getAttribute("data-entity-type") || "movement";
340357
const label = this.querySelector("strong").textContent;
341-
const entityType = await resoudre_type_entite(entityId, hintedType);
358+
const mode = this.getAttribute("data-search-mode") || "";
359+
if (mode === "nationality" || mode === "material") {
360+
searchDropdown.classList.add("is-active");
361+
searchDropdown.innerHTML = '<div style="padding: 12px; color: var(--text-muted);">This guided query is staged next. Switch back to Find entity to open this result directly.</div>';
362+
return;
363+
}
364+
const entityType = mode === "subject" ? "subject" : await resoudre_type_entite(entityId, hintedType);
342365
searchInput.value = label;
343366
searchDropdown.classList.remove("is-active");
344367
try {
@@ -426,4 +449,3 @@
426449
}
427450

428451
}
429-

0 commit comments

Comments
 (0)