synthos 0.7.1 → 0.8.0
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/README.md +215 -65
- package/default-pages/application.json +1 -0
- package/default-pages/json_tools.json +1 -1
- package/default-pages/oregon_trail.html +321 -0
- package/default-pages/oregon_trail.json +12 -0
- package/default-pages/sidebar_page.json +1 -0
- package/default-pages/solar_explorer.html +10 -18
- package/default-pages/solar_explorer.json +2 -2
- package/default-pages/two-panel_page.json +1 -0
- package/default-pages/us_map.html +192 -0
- package/default-pages/us_map.json +12 -0
- package/default-pages/us_map_1850.html +325 -0
- package/default-pages/us_map_1850.json +12 -0
- package/default-pages/western_cities_1850.html +526 -0
- package/default-pages/western_cities_1850.json +12 -0
- package/default-themes/{nebula-dawn.css → nebula-dawn.v2.css} +24 -0
- package/default-themes/{nebula-dusk.css → nebula-dusk.v2.css} +24 -0
- package/dist/agents/a2a/a2aProvider.d.ts +3 -0
- package/dist/agents/a2a/a2aProvider.d.ts.map +1 -0
- package/dist/agents/a2a/a2aProvider.js +126 -0
- package/dist/agents/a2a/a2aProvider.js.map +1 -0
- package/dist/agents/discovery.d.ts +30 -0
- package/dist/agents/discovery.d.ts.map +1 -0
- package/dist/agents/discovery.js +52 -0
- package/dist/agents/discovery.js.map +1 -0
- package/dist/agents/index.d.ts +7 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +19 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/openclaw/gatewayManager.d.ts +113 -0
- package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -0
- package/dist/agents/openclaw/gatewayManager.js +470 -0
- package/dist/agents/openclaw/gatewayManager.js.map +1 -0
- package/dist/agents/openclaw/openclawProvider.d.ts +3 -0
- package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -0
- package/dist/agents/openclaw/openclawProvider.js +239 -0
- package/dist/agents/openclaw/openclawProvider.js.map +1 -0
- package/dist/agents/openclaw/sshTunnelManager.d.ts +23 -0
- package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -0
- package/dist/agents/openclaw/sshTunnelManager.js +340 -0
- package/dist/agents/openclaw/sshTunnelManager.js.map +1 -0
- package/dist/agents/types.d.ts +64 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +6 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/connectors/airtable/connector.json +27 -0
- package/dist/connectors/alpha-vantage/connector.json +26 -0
- package/dist/connectors/brave-search/connector.json +26 -0
- package/dist/connectors/cloudinary/connector.json +27 -0
- package/dist/connectors/deepl/connector.json +28 -0
- package/dist/connectors/elevenlabs/connector.json +30 -0
- package/dist/connectors/giphy/connector.json +27 -0
- package/dist/connectors/github/connector.json +29 -0
- package/dist/connectors/huggingface/connector.json +27 -0
- package/dist/connectors/imgur/connector.json +29 -0
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/instagram/connector.json +43 -0
- package/dist/connectors/jira/connector.json +28 -0
- package/dist/connectors/mapbox/connector.json +26 -0
- package/dist/connectors/nasa/connector.json +27 -0
- package/dist/connectors/newsapi/connector.json +27 -0
- package/dist/connectors/notion/connector.json +28 -0
- package/dist/connectors/open-exchange-rates/connector.json +27 -0
- package/dist/connectors/openweathermap/connector.json +26 -0
- package/dist/connectors/pexels/connector.json +27 -0
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +42 -96
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/resend/connector.json +29 -0
- package/dist/connectors/rss2json/connector.json +27 -0
- package/dist/connectors/sendgrid/connector.json +27 -0
- package/dist/connectors/spoonacular/connector.json +28 -0
- package/dist/connectors/stability-ai/connector.json +27 -0
- package/dist/connectors/twilio/connector.json +28 -0
- package/dist/connectors/types.d.ts +23 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/unsplash/connector.json +27 -0
- package/dist/connectors/wolfram-alpha/connector.json +26 -0
- package/dist/connectors/youtube-data/connector.json +30 -0
- package/dist/files.d.ts +1 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +16 -1
- package/dist/files.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +28 -0
- package/dist/init.js.map +1 -1
- package/dist/migrations.d.ts +3 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +122 -138
- package/dist/migrations.js.map +1 -1
- package/dist/models/anthropic.d.ts +22 -0
- package/dist/models/anthropic.d.ts.map +1 -0
- package/dist/models/anthropic.js +76 -0
- package/dist/models/anthropic.js.map +1 -0
- package/dist/models/chainOfThought.d.ts +12 -0
- package/dist/models/chainOfThought.d.ts.map +1 -0
- package/dist/models/chainOfThought.js +45 -0
- package/dist/models/chainOfThought.js.map +1 -0
- package/dist/models/fireworksai.d.ts +30 -0
- package/dist/models/fireworksai.d.ts.map +1 -0
- package/dist/models/fireworksai.js +133 -0
- package/dist/models/fireworksai.js.map +1 -0
- package/dist/models/index.d.ts +7 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +19 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/logCompletePrompt.d.ts +3 -0
- package/dist/models/logCompletePrompt.d.ts.map +1 -0
- package/dist/models/logCompletePrompt.js +23 -0
- package/dist/models/logCompletePrompt.js.map +1 -0
- package/dist/models/openai.d.ts +24 -0
- package/dist/models/openai.d.ts.map +1 -0
- package/dist/models/openai.js +80 -0
- package/dist/models/openai.js.map +1 -0
- package/dist/models/providers.d.ts +1 -0
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/providers.js +12 -4
- package/dist/models/providers.js.map +1 -1
- package/dist/models/types.d.ts +34 -2
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js +16 -0
- package/dist/models/types.js.map +1 -1
- package/dist/models/utils.d.ts +6 -0
- package/dist/models/utils.d.ts.map +1 -0
- package/dist/models/utils.js +21 -0
- package/dist/models/utils.js.map +1 -0
- package/dist/scripts.d.ts +2 -1
- package/dist/scripts.d.ts.map +1 -1
- package/dist/scripts.js +4 -3
- package/dist/scripts.js.map +1 -1
- package/dist/service/createCompletePrompt.d.ts +1 -1
- package/dist/service/createCompletePrompt.d.ts.map +1 -1
- package/dist/service/createCompletePrompt.js +9 -6
- package/dist/service/createCompletePrompt.js.map +1 -1
- package/dist/service/generateImage.d.ts +1 -1
- package/dist/service/generateImage.d.ts.map +1 -1
- package/dist/service/generateImage.js +3 -3
- package/dist/service/generateImage.js.map +1 -1
- package/dist/service/server.d.ts.map +1 -1
- package/dist/service/server.js +3 -0
- package/dist/service/server.js.map +1 -1
- package/dist/service/transformPage.d.ts +4 -2
- package/dist/service/transformPage.d.ts.map +1 -1
- package/dist/service/transformPage.js +74 -6
- package/dist/service/transformPage.js.map +1 -1
- package/dist/service/useAgentRoutes.d.ts +4 -0
- package/dist/service/useAgentRoutes.d.ts.map +1 -0
- package/dist/service/useAgentRoutes.js +389 -0
- package/dist/service/useAgentRoutes.js.map +1 -0
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +157 -16
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.d.ts.map +1 -1
- package/dist/service/useConnectorRoutes.js +14 -3
- package/dist/service/useConnectorRoutes.js.map +1 -1
- package/dist/service/useGatewayRoutes.d.ts +4 -0
- package/dist/service/useGatewayRoutes.d.ts.map +1 -0
- package/dist/service/useGatewayRoutes.js +168 -0
- package/dist/service/useGatewayRoutes.js.map +1 -0
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +16 -5
- package/dist/service/usePageRoutes.js.map +1 -1
- package/dist/settings.d.ts +2 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +4 -8
- package/dist/settings.js.map +1 -1
- package/dist/themes.d.ts +14 -0
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +86 -13
- package/dist/themes.js.map +1 -1
- package/package.json +10 -5
- package/page-scripts/helpers-v2.js +222 -0
- package/page-scripts/page-v2.js +656 -0
- package/required-pages/builder.html +1 -27
- package/required-pages/pages.html +745 -22
- package/required-pages/settings.html +819 -21
- package/required-pages/synthos_apis.html +56 -1
- package/src/agents/a2a/a2aProvider.ts +110 -0
- package/src/agents/discovery.ts +74 -0
- package/src/agents/index.ts +6 -0
- package/src/agents/openclaw/gatewayManager.ts +559 -0
- package/src/agents/openclaw/openclawProvider.ts +261 -0
- package/src/agents/openclaw/sshTunnelManager.ts +385 -0
- package/src/agents/types.ts +82 -0
- package/src/connectors/airtable/connector.json +27 -0
- package/src/connectors/alpha-vantage/connector.json +26 -0
- package/src/connectors/brave-search/connector.json +26 -0
- package/src/connectors/cloudinary/connector.json +27 -0
- package/src/connectors/deepl/connector.json +28 -0
- package/src/connectors/elevenlabs/connector.json +30 -0
- package/src/connectors/giphy/connector.json +27 -0
- package/src/connectors/github/connector.json +29 -0
- package/src/connectors/huggingface/connector.json +27 -0
- package/src/connectors/imgur/connector.json +29 -0
- package/src/connectors/index.ts +2 -0
- package/src/connectors/instagram/connector.json +43 -0
- package/src/connectors/jira/connector.json +28 -0
- package/src/connectors/mapbox/connector.json +26 -0
- package/src/connectors/nasa/connector.json +27 -0
- package/src/connectors/newsapi/connector.json +27 -0
- package/src/connectors/notion/connector.json +28 -0
- package/src/connectors/open-exchange-rates/connector.json +27 -0
- package/src/connectors/openweathermap/connector.json +26 -0
- package/src/connectors/pexels/connector.json +27 -0
- package/src/connectors/registry.ts +21 -97
- package/src/connectors/resend/connector.json +29 -0
- package/src/connectors/rss2json/connector.json +27 -0
- package/src/connectors/sendgrid/connector.json +27 -0
- package/src/connectors/spoonacular/connector.json +28 -0
- package/src/connectors/stability-ai/connector.json +27 -0
- package/src/connectors/twilio/connector.json +28 -0
- package/src/connectors/types.ts +25 -0
- package/src/connectors/unsplash/connector.json +27 -0
- package/src/connectors/wolfram-alpha/connector.json +26 -0
- package/src/connectors/youtube-data/connector.json +30 -0
- package/src/files.ts +14 -0
- package/src/init.ts +27 -0
- package/src/migrations.ts +121 -138
- package/src/models/anthropic.ts +89 -0
- package/src/models/chainOfThought.ts +56 -0
- package/src/models/fireworksai.ts +136 -0
- package/src/models/index.ts +7 -1
- package/src/models/logCompletePrompt.ts +25 -0
- package/src/models/openai.ts +90 -0
- package/src/models/providers.ts +12 -3
- package/src/models/types.ts +67 -2
- package/src/models/utils.ts +16 -0
- package/src/scripts.ts +2 -2
- package/src/service/createCompletePrompt.ts +3 -1
- package/src/service/generateImage.ts +2 -2
- package/src/service/server.ts +4 -0
- package/src/service/transformPage.ts +81 -8
- package/src/service/useAgentRoutes.ts +423 -0
- package/src/service/useApiRoutes.ts +173 -18
- package/src/service/useConnectorRoutes.ts +14 -3
- package/src/service/usePageRoutes.ts +20 -6
- package/src/settings.ts +6 -10
- package/src/themes.ts +84 -12
- package/tests/README.md +12 -0
- package/tests/anthropic.spec.ts +84 -0
- package/tests/chainOfThought.spec.ts +108 -0
- package/tests/ensureScripts.spec.ts +82 -0
- package/tests/files.spec.ts +233 -0
- package/tests/fireworksai.spec.ts +92 -0
- package/tests/logCompletePrompt.spec.ts +74 -0
- package/tests/migrations.spec.ts +169 -0
- package/tests/openai.spec.ts +71 -0
- package/tests/pages.spec.ts +328 -0
- package/tests/providers.spec.ts +144 -0
- package/tests/scripts.spec.ts +209 -0
- package/tests/transformPage.spec.ts +931 -0
- package/tests/types.spec.ts +23 -0
- package/default-pages/app_builder.json +0 -1
- package/default-pages/sidebar_builder.json +0 -1
- package/default-pages/two-panel_builder.json +0 -1
- package/images/home.png +0 -0
- package/images/page-management.png +0 -0
- package/images/settings.png +0 -0
- package/images/synthos-square.png +0 -0
- /package/default-pages/{app_builder.html → application.html} +0 -0
- /package/default-pages/{sidebar_builder.html → sidebar_page.html} +0 -0
- /package/default-pages/{two-panel_builder.html → two-panel_page.html} +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.idle-container{position:absolute;width:100%;height:100%;pointer-events:none;opacity:1;transition:opacity 1s ease-out}.idle-container.hidden{opacity:0}.breathing-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle,rgba(102,126,234,.15) 0,transparent 70%);animation:4s ease-in-out infinite breathe}.breathing-orb::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;border-radius:50%;background:rgba(183,148,246,.6);box-shadow:0 0 20px rgba(183,148,246,.4);animation:4s ease-in-out infinite core-pulse}@keyframes breathe{0%,100%{width:80px;height:80px;opacity:.3}50%{width:120px;height:120px;opacity:.6}}@keyframes core-pulse{0%,100%{opacity:.4;box-shadow:0 0 20px rgba(183,148,246,.3)}50%{opacity:.8;box-shadow:0 0 30px rgba(183,148,246,.5)}}.orbit-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200px;height:200px;border:1px solid rgba(102,126,234,.1);border-radius:50%;animation:20s linear infinite orbit-rotate}.orbit-ring::after{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;background:rgba(240,147,251,.5);border-radius:50%;box-shadow:0 0 10px rgba(240,147,251,.3)}@keyframes orbit-rotate{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
<script id="page-info" src="/api/page-info.js?page=builder"></script>
|
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" id="topojson-lib"></script><style id="us-map-styles">
|
|
13
|
+
#us-map-container {
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 100%;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
background: var(--bg-primary);
|
|
21
|
+
position: relative;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
#us-map-container h2 {
|
|
25
|
+
color: var(--text-primary);
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 12px 0 4px 0;
|
|
28
|
+
font-size: 1.4rem;
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
letter-spacing: 0.02em;
|
|
31
|
+
}
|
|
32
|
+
#us-map-svg {
|
|
33
|
+
width: 100%;
|
|
34
|
+
flex: 1;
|
|
35
|
+
min-height: 0;
|
|
36
|
+
}
|
|
37
|
+
#us-map-svg .state {
|
|
38
|
+
fill: var(--accent-primary);
|
|
39
|
+
stroke: var(--bg-primary);
|
|
40
|
+
stroke-width: 1;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
transition: fill 0.2s ease;
|
|
43
|
+
}
|
|
44
|
+
#us-map-svg .state:hover {
|
|
45
|
+
fill: var(--accent-tertiary);
|
|
46
|
+
}
|
|
47
|
+
#us-map-svg .state-border {
|
|
48
|
+
fill: none;
|
|
49
|
+
stroke: var(--bg-primary);
|
|
50
|
+
stroke-width: 1.5;
|
|
51
|
+
}
|
|
52
|
+
#us-map-svg .nation-border {
|
|
53
|
+
fill: none;
|
|
54
|
+
stroke: var(--text-secondary);
|
|
55
|
+
stroke-width: 1.5;
|
|
56
|
+
}
|
|
57
|
+
#map-tooltip {
|
|
58
|
+
position: absolute;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
background: var(--bg-tertiary);
|
|
61
|
+
color: var(--text-primary);
|
|
62
|
+
border: 1px solid var(--border-color);
|
|
63
|
+
border-radius: 6px;
|
|
64
|
+
padding: 6px 12px;
|
|
65
|
+
font-size: 0.9rem;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
|
68
|
+
opacity: 0;
|
|
69
|
+
transition: opacity 0.15s ease;
|
|
70
|
+
z-index: 10;
|
|
71
|
+
}
|
|
72
|
+
.light-mode #us-map-svg .state {
|
|
73
|
+
fill: var(--accent-primary);
|
|
74
|
+
stroke: var(--bg-primary);
|
|
75
|
+
}
|
|
76
|
+
.light-mode #us-map-svg .state:hover {
|
|
77
|
+
fill: var(--accent-tertiary);
|
|
78
|
+
}
|
|
79
|
+
</style></head>
|
|
80
|
+
<body>
|
|
81
|
+
<div class="chat-panel" data-locked="true">
|
|
82
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
83
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
84
|
+
<div class="chat-message" id="defaultGreeting"><p><strong>SynthOS:</strong> Here's your interactive US map! You can customize it by adding data to visualize — just tell me what you'd like to overlay. Here are a couple of ideas:
|
|
85
|
+
<ul style="margin:8px 0 4px 0;padding-left:20px;">
|
|
86
|
+
<li><strong>Population by state</strong> — color-coded choropleth showing population density</li>
|
|
87
|
+
<li><strong>Election results</strong> — red/blue map of voting outcomes by state</li>
|
|
88
|
+
<li><strong>Custom data</strong> — provide your own values per state (sales figures, temperatures, rankings, etc.)</li>
|
|
89
|
+
</ul>
|
|
90
|
+
What data would you like to see on the map?</p></div>
|
|
91
|
+
|
|
92
|
+
</div>
|
|
93
|
+
<div class="link-group" data-locked="true">
|
|
94
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
95
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
96
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
97
|
+
</div>
|
|
98
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
99
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
100
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
101
|
+
</form>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="viewer-panel full-viewer" id="viewerPanel"><div id="us-map-container">
|
|
104
|
+
<h2>United States</h2>
|
|
105
|
+
<svg id="us-map-svg"></svg>
|
|
106
|
+
<div id="map-tooltip"></div>
|
|
107
|
+
</div>
|
|
108
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div></div>
|
|
109
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
110
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
111
|
+
<script id="idle-animation">function hideIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.classList.add("hidden"),setTimeout(()=>{idleContainer.style.display="none"},1e3))}function showIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.style.display="block",setTimeout(()=>{idleContainer.classList.remove("hidden")},10))}</script>
|
|
112
|
+
<button class="chat-toggle" aria-label="Toggle chat panel">
|
|
113
|
+
<span class="chat-toggle-dots">
|
|
114
|
+
<span class="chat-toggle-dot"></span>
|
|
115
|
+
<span class="chat-toggle-dot"></span>
|
|
116
|
+
<span class="chat-toggle-dot"></span>
|
|
117
|
+
</span>
|
|
118
|
+
</button>
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
<script id="us-map-script">
|
|
122
|
+
(function() {
|
|
123
|
+
function initMap() {
|
|
124
|
+
if (typeof topojson === 'undefined') {
|
|
125
|
+
setTimeout(initMap, 100);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const container = document.getElementById('us-map-container');
|
|
130
|
+
const svg = d3.select('#us-map-svg');
|
|
131
|
+
const tooltip = document.getElementById('map-tooltip');
|
|
132
|
+
|
|
133
|
+
const width = container.clientWidth;
|
|
134
|
+
const height = container.clientHeight - 50;
|
|
135
|
+
|
|
136
|
+
svg.attr('viewBox', `0 0 ${width} ${height}`)
|
|
137
|
+
.attr('preserveAspectRatio', 'xMidYMid meet');
|
|
138
|
+
|
|
139
|
+
const projection = d3.geoAlbersUsa()
|
|
140
|
+
.fitSize([width - 40, height - 20], { type: 'Sphere' })
|
|
141
|
+
.translate([width / 2, height / 2]);
|
|
142
|
+
|
|
143
|
+
const path = d3.geoPath().projection(projection);
|
|
144
|
+
|
|
145
|
+
d3.json('https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json').then(function(us) {
|
|
146
|
+
const states = topojson.feature(us, us.objects.states);
|
|
147
|
+
|
|
148
|
+
svg.selectAll('.state')
|
|
149
|
+
.data(states.features)
|
|
150
|
+
.enter()
|
|
151
|
+
.append('path')
|
|
152
|
+
.attr('class', 'state')
|
|
153
|
+
.attr('d', path)
|
|
154
|
+
.on('mousemove', function(event, d) {
|
|
155
|
+
tooltip.textContent = d.properties.name;
|
|
156
|
+
tooltip.style.opacity = '1';
|
|
157
|
+
const rect = container.getBoundingClientRect();
|
|
158
|
+
const x = event.clientX - rect.left + 12;
|
|
159
|
+
const y = event.clientY - rect.top - 30;
|
|
160
|
+
tooltip.style.left = x + 'px';
|
|
161
|
+
tooltip.style.top = y + 'px';
|
|
162
|
+
})
|
|
163
|
+
.on('mouseleave', function() {
|
|
164
|
+
tooltip.style.opacity = '0';
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
svg.append('path')
|
|
168
|
+
.datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b))
|
|
169
|
+
.attr('class', 'state-border')
|
|
170
|
+
.attr('d', path);
|
|
171
|
+
|
|
172
|
+
svg.append('path')
|
|
173
|
+
.datum(topojson.mesh(us, us.objects.nation))
|
|
174
|
+
.attr('class', 'nation-border')
|
|
175
|
+
.attr('d', path);
|
|
176
|
+
}).catch(function(err) {
|
|
177
|
+
console.error('Failed to load US map data:', err);
|
|
178
|
+
container.innerHTML = '<p style="color:var(--text-secondary);text-align:center;padding:2rem;">Failed to load map data. Please try again.</p>';
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
window.addEventListener('resize', function() {
|
|
182
|
+
const w = container.clientWidth;
|
|
183
|
+
const h = container.clientHeight - 50;
|
|
184
|
+
svg.attr('viewBox', `0 0 ${w} ${h}`);
|
|
185
|
+
projection.fitSize([w - 40, h - 20], { type: 'Sphere' }).translate([w / 2, h / 2]);
|
|
186
|
+
svg.selectAll('path').attr('d', path);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
initMap();
|
|
191
|
+
})();
|
|
192
|
+
</script><script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script><script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script></body></html>
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.idle-container{position:absolute;width:100%;height:100%;pointer-events:none;opacity:1;transition:opacity 1s ease-out}.idle-container.hidden{opacity:0}.breathing-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle,rgba(102,126,234,.15) 0,transparent 70%);animation:4s ease-in-out infinite breathe}.breathing-orb::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;border-radius:50%;background:rgba(183,148,246,.6);box-shadow:0 0 20px rgba(183,148,246,.4);animation:4s ease-in-out infinite core-pulse}@keyframes breathe{0%,100%{width:80px;height:80px;opacity:.3}50%{width:120px;height:120px;opacity:.6}}@keyframes core-pulse{0%,100%{opacity:.4;box-shadow:0 0 20px rgba(183,148,246,.3)}50%{opacity:.8;box-shadow:0 0 30px rgba(183,148,246,.5)}}.orbit-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200px;height:200px;border:1px solid rgba(102,126,234,.1);border-radius:50%;animation:20s linear infinite orbit-rotate}.orbit-ring::after{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;background:rgba(240,147,251,.5);border-radius:50%;box-shadow:0 0 10px rgba(240,147,251,.3)}@keyframes orbit-rotate{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
<script id="page-info" src="/api/page-info.js?page=builder"></script>
|
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" id="topojson-lib"></script><style id="us-map-styles">#us-map-container{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden}#us-map-header{min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;font-size:1.25rem;font-weight:600;color:var(--text-primary);width:100%;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0;position:relative}#us-map-svg-wrap{flex:1;width:100%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;position:relative}#us-map-svg-wrap svg{width:100%;height:100%;position:absolute;top:0;left:0;cursor:grab}#us-map-svg-wrap svg:active{cursor:grabbing}.region-path{cursor:pointer;transition:filter .2s ease;stroke-linejoin:round}.region-path:hover{filter:brightness(1.25)}.region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20;stroke-width:1.2}.region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100;stroke-width:1.5}.region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c;stroke-width:1.5}.region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e;stroke-width:1.5}.region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723;stroke-width:1.5}.light-mode .region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20}.light-mode .region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c}.light-mode .region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100}.light-mode .region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c}.light-mode .region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c}.light-mode .region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e}.light-mode .region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723}#map-tooltip{position:absolute;pointer-events:none;background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:6px;padding:6px 12px;font-size:0.85rem;font-weight:500;box-shadow:0 2px 8px rgba(0,0,0,0.12);opacity:0;transition:opacity .15s ease;z-index:10;white-space:nowrap}#hist-legend{position:absolute;bottom:16px;left:16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px;font-size:0.75rem;color:var(--text-primary);z-index:5;box-shadow:0 2px 8px rgba(0,0,0,0.1)}#hist-legend .legend-item{display:flex;align-items:center;gap:8px;margin-bottom:4px}#hist-legend .legend-item:last-child{margin-bottom:0}#hist-legend .legend-swatch{width:14px;height:14px;border-radius:3px;flex-shrink:0}#zoom-controls{position:absolute;top:16px;right:16px;display:flex;flex-direction:column;gap:4px;z-index:5}#zoom-controls button{width:36px;height:36px;border:1px solid var(--border-color);border-radius:6px;background:var(--bg-tertiary);color:var(--text-primary);font-size:1.2rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(0,0,0,0.1);transition:background .15s ease}#zoom-controls button:hover{background:var(--bg-secondary)}</style><style id="detail-card-styles">.city-marker{cursor:pointer}.city-marker .marker-glow{fill:var(--accent-primary);opacity:0.15;transition:opacity .2s ease}.city-marker .marker-dot{fill:var(--accent-primary);transition:r .2s ease}.city-marker .marker-hit{fill:transparent;cursor:pointer}.city-marker:hover .marker-glow{opacity:0.35}.city-marker:hover .marker-dot{r:3.5}.city-marker .marker-label{font-size:8px;font-weight:600;fill:var(--text-primary);text-anchor:middle;pointer-events:none;paint-order:stroke;stroke:var(--bg-primary);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round}#detail-card{position:absolute;top:16px;right:16px;width:340px;max-height:calc(100% - 40px);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:20;opacity:0;transform:translateY(-10px) scale(0.97);pointer-events:none;transition:opacity .25s ease,transform .25s ease;overflow:hidden;display:flex;flex-direction:column}#detail-card.visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}#detail-card .card-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 8px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}#detail-card .card-header h3{margin:0;font-size:0.95rem;font-weight:700;color:var(--text-primary);line-height:1.3}#detail-card .card-header .card-close{background:none;border:none;font-size:1.3rem;cursor:pointer;color:var(--text-secondary);padding:0 2px;line-height:1;border-radius:4px;transition:background .15s ease}#detail-card .card-header .card-close:hover{background:rgba(0,0,0,0.08)}#detail-card .card-subtitle{font-size:0.72rem;color:var(--text-secondary);margin:2px 0 0;font-weight:500}#detail-card .card-tabs{display:flex;border-bottom:1px solid var(--border-color);flex-shrink:0}#detail-card .card-tab{flex:1;padding:8px 10px;font-size:0.78rem;font-weight:600;text-align:center;cursor:pointer;border:none;background:none;color:var(--text-secondary);transition:all .15s ease;border-bottom:2px solid transparent;position:relative}#detail-card .card-tab:hover{color:var(--text-primary);background:rgba(0,0,0,0.03)}#detail-card .card-tab.active{color:var(--accent-primary);border-bottom-color:var(--accent-primary)}#detail-card .card-tab-icon{margin-right:4px}#detail-card .tab-content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0}#detail-card .tab-pane{display:none;flex:1;overflow-y:auto;flex-direction:column;min-height:0}#detail-card .tab-pane.active{display:flex}#detail-card .facts-pane{padding:12px 14px;overflow-y:auto}#detail-card .fact-row{display:flex;gap:8px;margin-bottom:8px;align-items:flex-start}#detail-card .fact-icon{font-size:1rem;flex-shrink:0;width:22px;text-align:center;margin-top:1px}#detail-card .fact-content{flex:1}#detail-card .fact-label{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-secondary);font-weight:600;margin-bottom:1px}#detail-card .fact-value{font-size:0.8rem;color:var(--text-primary);line-height:1.4}#detail-card .card-divider{height:1px;background:var(--border-color);margin:4px 0 8px}#detail-card .card-description{font-size:0.78rem;color:var(--text-primary);line-height:1.5;margin-top:2px}#detail-card .chat-pane{flex-direction:column;min-height:0;flex:1}#detail-card .chat-messages-area{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:120px}#detail-card .chat-welcome{text-align:center;padding:16px 8px;color:var(--text-secondary);font-size:0.78rem;line-height:1.5}#detail-card .chat-welcome strong{color:var(--text-primary)}#detail-card .chat-bubble{max-width:92%;padding:8px 11px;border-radius:12px;font-size:0.78rem;line-height:1.45;word-wrap:break-word;animation:bubbleIn .2s ease}#detail-card .chat-bubble.user-bubble{align-self:flex-end;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px}#detail-card .chat-bubble.ai-bubble{align-self:flex-start;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-bottom-left-radius:4px}#detail-card .chat-bubble.ai-bubble.loading{opacity:0.7}#detail-card .typing-dots{display:inline-flex;gap:3px;padding:2px 0}#detail-card .typing-dots span{width:5px;height:5px;border-radius:50%;background:var(--text-secondary);animation:dotBounce .6s ease-in-out infinite}#detail-card .typing-dots span:nth-child(2){animation-delay:.15s}#detail-card .typing-dots span:nth-child(3){animation-delay:.3s}@keyframes dotBounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}@keyframes bubbleIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}#detail-card .chat-input-area{display:flex;gap:6px;padding:8px 10px;border-top:1px solid var(--border-color);background:var(--bg-tertiary);flex-shrink:0;border-radius:0 0 12px 12px}#detail-card .chat-input-field{flex:1;border:1px solid var(--border-color);border-radius:18px;padding:7px 12px;font-size:0.78rem;background:var(--bg-primary);color:var(--text-primary);outline:none;transition:border-color .15s ease;font-family:inherit}#detail-card .chat-input-field:focus{border-color:var(--accent-primary)}#detail-card .chat-input-field::placeholder{color:var(--text-secondary);opacity:0.7}#detail-card .chat-send-btn{width:32px;height:32px;border-radius:50%;border:none;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;font-size:0.9rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s ease,opacity .15s ease;flex-shrink:0}#detail-card .chat-send-btn:hover{transform:scale(1.08)}#detail-card .chat-send-btn:disabled{opacity:0.4;cursor:not-allowed;transform:none}#detail-card .suggested-questions{display:flex;flex-wrap:wrap;gap:4px;padding:0 12px 8px}#detail-card .suggested-q{font-size:0.68rem;padding:4px 8px;border-radius:12px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-secondary);cursor:pointer;transition:all .15s ease;white-space:nowrap}#detail-card .suggested-q:hover{color:var(--accent-primary);border-color:var(--accent-primary)}#card-backdrop{display:none;position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.2);z-index:15}#card-backdrop.visible{display:block}</style></head>
|
|
13
|
+
<body>
|
|
14
|
+
<div class="chat-panel" data-locked="true">
|
|
15
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
16
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
17
|
+
<div class="chat-message" id="defaultGreeting"><p><strong>SynthOS:</strong> Welcome! This is an interactive map of the United States circa the 1850s, showing how the country was politically organized after the Compromise of 1850. It's designed for interactive exploration — here are a few things you could try:
|
|
18
|
+
<ul style="margin-top:8px;margin-bottom:4px;padding-left:20px;">
|
|
19
|
+
<li><strong>Population density overlay</strong> — Visualize estimated population distribution across states and territories in the 1850s</li>
|
|
20
|
+
<li><strong>Railroad & transportation routes</strong> — Map the major rail lines, trails, and waterways that connected the nation</li>
|
|
21
|
+
<li><strong>Climate & geography zones</strong> — Overlay biomes, elevation, or average rainfall data onto the territorial boundaries</li>
|
|
22
|
+
<li><strong>Statehood timeline animation</strong> — Animate how territories became states over time from 1776 to 1860</li>
|
|
23
|
+
<li><strong>Resource & agriculture map</strong> — Show where key crops, minerals, and natural resources were concentrated</li>
|
|
24
|
+
</ul>
|
|
25
|
+
Just tell me what you'd like to explore!</p></div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="link-group" data-locked="true">
|
|
28
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
29
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
30
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
31
|
+
</div>
|
|
32
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
33
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
34
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
35
|
+
</form>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="viewer-panel full-viewer" id="viewerPanel"><div id="us-map-container"><div id="us-map-header">United States — Circa 1850</div><div id="us-map-svg-wrap"><div id="hist-legend"><div style="font-weight:600;margin-bottom:6px;">Circa 1850</div><div class="legend-item"><div class="legend-swatch" style="background:rgba(46,125,50,0.6);border:1px solid #1b5e20;"></div><span>States (31)</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(230,81,0,0.55);border:1px solid #bf360c;"></div><span>Oregon Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,152,0,0.55);border:1px solid #e65100;"></div><span>Minnesota Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,111,0,0.5);border:1px solid #bf360c;"></div><span>Utah Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(239,108,0,0.55);border:1px solid #bf360c;"></div><span>New Mexico Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(141,110,99,0.5);border:1px solid #4e342e;"></div><span>Unorganized Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(121,85,72,0.55);border:1px solid #3e2723;"></div><span>Indian Territory</span></div></div><div id="zoom-controls"><button id="zoom-in-btn" title="Zoom in">+</button><button id="zoom-out-btn" title="Zoom out">−</button><button id="zoom-reset-btn" title="Reset view" style="font-size:0.75rem;">⟲</button></div></div><div id="map-tooltip"></div><div id="detail-card"><div class="card-header"><div><h3 id="card-title"></h3><div class="card-subtitle" id="card-subtitle"></div></div><button class="card-close" id="card-close-btn" aria-label="Close">×</button></div><div class="card-tabs"><button class="card-tab active" data-tab="facts"><span class="card-tab-icon">📋</span>Facts</button><button class="card-tab" data-tab="chat"><span class="card-tab-icon">💬</span>Ask Me</button></div><div class="tab-content"><div class="tab-pane facts-pane active" id="facts-pane"><div id="card-body"></div></div><div class="tab-pane chat-pane" id="chat-pane"><div class="chat-messages-area" id="detail-chat-messages"></div><div class="suggested-questions" id="detail-suggested-q"></div><div class="chat-input-area"><input type="text" class="chat-input-field" id="detail-chat-input" placeholder="Ask me anything..." autocomplete="off"><button class="chat-send-btn" id="detail-chat-send" aria-label="Send">➤</button></div></div></div></div><div id="card-backdrop"></div></div><div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div></div>
|
|
38
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
39
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
40
|
+
<script id="idle-animation">function hideIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.classList.add("hidden"),setTimeout(()=>{idleContainer.style.display="none"},1e3))}function showIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.style.display="block",setTimeout(()=>{idleContainer.classList.remove("hidden")},10))}</script>
|
|
41
|
+
<button class="chat-toggle" aria-label="Toggle chat panel">
|
|
42
|
+
<span class="chat-toggle-dots">
|
|
43
|
+
<span class="chat-toggle-dot"></span>
|
|
44
|
+
<span class="chat-toggle-dot"></span>
|
|
45
|
+
<span class="chat-toggle-dot"></span>
|
|
46
|
+
</span>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
<script id="us-map-script">(function(){var FIPS_TO_NAME={1:'Alabama',2:'Alaska',4:'Arizona',5:'Arkansas',6:'California',8:'Colorado',9:'Connecticut',10:'Delaware',11:'District of Columbia',12:'Florida',13:'Georgia',15:'Hawaii',16:'Idaho',17:'Illinois',18:'Indiana',19:'Iowa',20:'Kansas',21:'Kentucky',22:'Louisiana',23:'Maine',24:'Maryland',25:'Massachusetts',26:'Michigan',27:'Minnesota',28:'Mississippi',29:'Missouri',30:'Montana',31:'Nebraska',32:'Nevada',33:'New Hampshire',34:'New Jersey',35:'New Mexico',36:'New York',37:'North Carolina',38:'North Dakota',39:'Ohio',40:'Oklahoma',41:'Oregon',42:'Pennsylvania',44:'Rhode Island',45:'South Carolina',46:'South Dakota',47:'Tennessee',48:'Texas',49:'Utah',50:'Vermont',51:'Virginia',53:'Washington',54:'West Virginia',55:'Wisconsin',56:'Wyoming'};
|
|
51
|
+
var STATES_1850=[9,10,13,24,25,33,34,36,37,42,44,45,51,50,21,47,39,22,18,28,17,1,23,29,5,26,12,48,19,55,6,54,11];
|
|
52
|
+
var OREGON_TERR=[41,53,16];
|
|
53
|
+
var MINNESOTA_TERR=[27];
|
|
54
|
+
var UTAH_TERR=[49,32];
|
|
55
|
+
var NEWMEXICO_TERR=[35,4];
|
|
56
|
+
var UNORGANIZED=[31,20,38,46,56,30,8];
|
|
57
|
+
var INDIAN_TERR=[40];
|
|
58
|
+
var TERRITORY_GROUPS=[{fips:OREGON_TERR,name:'Oregon Territory',cls:'region-oregon'},{fips:MINNESOTA_TERR,name:'Minnesota Territory',cls:'region-minnesota'},{fips:UTAH_TERR,name:'Utah Territory',cls:'region-utah'},{fips:NEWMEXICO_TERR,name:'New Mexico Territory',cls:'region-newmexico'},{fips:UNORGANIZED,name:'Unorganized Territory',cls:'region-unorganized'},{fips:INDIAN_TERR,name:'Indian Territory',cls:'region-indian'}];
|
|
59
|
+
function init(){var wrap=document.getElementById('us-map-svg-wrap');if(!wrap)return;var tooltip=document.getElementById('map-tooltip');wrap.querySelectorAll('svg').forEach(function(s){s.remove();});
|
|
60
|
+
var svg=d3.select(wrap).append('svg').attr('id','hist-map-svg').attr('viewBox','0 0 960 600').attr('preserveAspectRatio','xMidYMid meet').style('padding','0').style('box-sizing','border-box');
|
|
61
|
+
var g=svg.append('g').attr('id','map-zoom-group');
|
|
62
|
+
var zoom=d3.zoom().scaleExtent([1,12]).on('zoom',function(event){g.attr('transform',event.transform);});
|
|
63
|
+
svg.call(zoom);
|
|
64
|
+
svg.on('dblclick.zoom',null);
|
|
65
|
+
var projection=d3.geoAlbersUsa().scale(1280).translate([480,300]);var path=d3.geoPath().projection(projection);
|
|
66
|
+
d3.json('https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json').then(function(us){var statesGeo=us.objects.states;var allGeometries=statesGeo.geometries;
|
|
67
|
+
function geomsForFips(fipsList){return allGeometries.filter(function(g){return fipsList.indexOf(+g.id)!==-1;});}
|
|
68
|
+
var stateGeoms=geomsForFips(STATES_1850);stateGeoms.forEach(function(geom){var feature=topojson.feature(us,geom);g.append('path').datum(feature).attr('class','region-path region-state').attr('d',path).on('mousemove',function(event){var name=FIPS_TO_NAME[+geom.id]||'Unknown';tooltip.textContent=name+' (State)';tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';});});
|
|
69
|
+
TERRITORY_GROUPS.forEach(function(group){var geoms=geomsForFips(group.fips);if(geoms.length===0)return;var merged=topojson.merge(us,geoms);g.append('path').datum(merged).attr('class','region-path '+group.cls).attr('d',path).on('mousemove',function(event){tooltip.textContent=group.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';});});
|
|
70
|
+
var stateGeoms2=geomsForFips(STATES_1850);g.append('path').datum(topojson.mesh(us,{type:'GeometryCollection',geometries:stateGeoms2},function(a,b){return a!==b&&STATES_1850.indexOf(+a.id)!==-1&&STATES_1850.indexOf(+b.id)!==-1;})).attr('fill','none').attr('stroke','rgba(27,94,32,0.4)').attr('stroke-width','0.5').attr('d',path).style('pointer-events','none');
|
|
71
|
+
}).catch(function(err){console.error('Failed to load US map data:',err);wrap.innerHTML='<p style="color:var(--text-secondary);text-align:center;">Failed to load map data. Please try again.</p>';});}
|
|
72
|
+
if(typeof topojson!=='undefined'){init();}else{var check=setInterval(function(){if(typeof topojson!=='undefined'){clearInterval(check);init();}},100);setTimeout(function(){clearInterval(check);},10000);}
|
|
73
|
+
})();</script><script id="zoom-controls-script">(function(){function waitForSvg(){var svg=d3.select('#hist-map-svg');if(svg.empty()){setTimeout(waitForSvg,200);return;}var zoomBehavior=null;svg.each(function(){var z=d3.zoomTransform(this);if(z)zoomBehavior=true;});var zoomHandler=d3.zoom().scaleExtent([1,12]).on('zoom',function(event){d3.select('#map-zoom-group').attr('transform',event.transform);});document.getElementById('zoom-in-btn').addEventListener('click',function(){svg.transition().duration(300).call(zoomHandler.scaleBy,1.5);});document.getElementById('zoom-out-btn').addEventListener('click',function(){svg.transition().duration(300).call(zoomHandler.scaleBy,1/1.5);});document.getElementById('zoom-reset-btn').addEventListener('click',function(){svg.transition().duration(400).call(zoomHandler.transform,d3.zoomIdentity);});svg.call(zoomHandler);}waitForSvg();})();</script><script id="detail-card-script">(function(){
|
|
74
|
+
/* ─── DETAIL CARD SCRIPT ───────────────────────────────────────────────
|
|
75
|
+
* RENDERING ORDER CONTRACT:
|
|
76
|
+
* This script executes AFTER us-map-script (which starts async TopoJSON
|
|
77
|
+
* loading) and zoom-controls-script. waitForMap() polls until at least
|
|
78
|
+
* one .region-path exists in the SVG before appending markers. This
|
|
79
|
+
* guarantees markers are the LAST children in the zoom group so they
|
|
80
|
+
* paint on top of map paths and receive click events.
|
|
81
|
+
* ──────────────────────────────────────────────────────────────────── */
|
|
82
|
+
|
|
83
|
+
/* ─── MARKER DATA ──────────────────────────────────────────────────────
|
|
84
|
+
* Each entry describes a point of interest to plot on the map.
|
|
85
|
+
* Expected shape:
|
|
86
|
+
* {
|
|
87
|
+
* name: 'City Name', // display name (used as card title)
|
|
88
|
+
* lat: 40.7128, // latitude (decimal degrees)
|
|
89
|
+
* lon: -74.0060, // longitude (negative = west)
|
|
90
|
+
* subtitle: 'A short tagline', // shown below the title in the header
|
|
91
|
+
* description: 'Longer text.', // shown at bottom of the facts pane
|
|
92
|
+
* facts: [ // array of fact rows for the card
|
|
93
|
+
* { icon: '📏', label: 'Elevation', value: '5,280 ft' }
|
|
94
|
+
* ],
|
|
95
|
+
* suggestedQuestions: [ // (optional) starter questions for chat
|
|
96
|
+
* 'What was life like here?'
|
|
97
|
+
* ]
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* The LLM should populate this array with location-specific data.
|
|
101
|
+
* ──────────────────────────────────────────────────────────────────── */
|
|
102
|
+
var MARKER_DATA = [
|
|
103
|
+
// Example (replace with real data):
|
|
104
|
+
// { name: 'St. Louis', lat: 38.627, lon: -90.199, subtitle: 'Gateway to the West',
|
|
105
|
+
// description: 'A major hub for westward expansion.',
|
|
106
|
+
// facts: [{ icon: '👥', label: 'Population', value: '~78,000 by 1850' }],
|
|
107
|
+
// suggestedQuestions: ['What was St. Louis like in the 1850s?'] }
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
/* ─── STATE ─────────────────────────────────────────────────────────── */
|
|
111
|
+
var currentItem = null;
|
|
112
|
+
var chatHistory = [];
|
|
113
|
+
|
|
114
|
+
/* ─── RENDERING ORDER GUARD ───────────────────────────────────────────
|
|
115
|
+
* ⚠️ Do NOT remove this guard. Markers MUST be appended AFTER the async
|
|
116
|
+
* TopoJSON fetch finishes and paths are drawn. Without this check the
|
|
117
|
+
* SVG paint order would put paths on top of markers, blocking clicks.
|
|
118
|
+
* ──────────────────────────────────────────────────────────────────── */
|
|
119
|
+
function waitForMap(){
|
|
120
|
+
var g=document.getElementById('map-zoom-group');
|
|
121
|
+
if(!g||!g.querySelector('.region-path')){setTimeout(waitForMap,300);return;}
|
|
122
|
+
plotMarkers(g);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* ─── PLOT MARKERS ────────────────────────────────────────────────────
|
|
126
|
+
* Markers are appended as the LAST children of the zoom group so they
|
|
127
|
+
* sit on top of all map paths in SVG paint order. Do not move this
|
|
128
|
+
* call earlier in the execution chain.
|
|
129
|
+
* ──────────────────────────────────────────────────────────────────── */
|
|
130
|
+
function plotMarkers(zoomGroup){
|
|
131
|
+
if(MARKER_DATA.length===0)return;
|
|
132
|
+
var projection=d3.geoAlbersUsa().scale(1280).translate([480,300]);
|
|
133
|
+
var g=d3.select(zoomGroup);
|
|
134
|
+
MARKER_DATA.forEach(function(item){
|
|
135
|
+
var pt=projection([item.lon,item.lat]);
|
|
136
|
+
if(!pt)return;
|
|
137
|
+
var marker=g.append('g').attr('class','city-marker').attr('transform','translate('+pt[0]+','+pt[1]+')');
|
|
138
|
+
marker.append('circle').attr('class','marker-glow').attr('r',8);
|
|
139
|
+
marker.append('circle').attr('class','marker-dot').attr('r',3);
|
|
140
|
+
marker.append('text').attr('class','marker-label').attr('y',-12).text(item.name);
|
|
141
|
+
marker.append('circle').attr('class','marker-hit').attr('r',14);
|
|
142
|
+
marker.on('click',function(event){event.stopPropagation();openCard(item);});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ─── CARD FUNCTIONS ──────────────────────────────────────────────── */
|
|
147
|
+
function openCard(item){
|
|
148
|
+
var card=document.getElementById('detail-card');
|
|
149
|
+
var backdrop=document.getElementById('card-backdrop');
|
|
150
|
+
if(!card)return;
|
|
151
|
+
document.getElementById('card-title').textContent=item.name;
|
|
152
|
+
document.getElementById('card-subtitle').textContent=item.subtitle||'';
|
|
153
|
+
var body=document.getElementById('card-body');
|
|
154
|
+
var html='';
|
|
155
|
+
if(item.facts&&item.facts.length){
|
|
156
|
+
item.facts.forEach(function(f){
|
|
157
|
+
html+='<div class="fact-row"><div class="fact-icon">'+f.icon+'</div><div class="fact-content"><div class="fact-label">'+f.label+'</div><div class="fact-value">'+f.value+'</div></div></div>';
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if(item.description){
|
|
161
|
+
if(html)html+='<div class="card-divider"></div>';
|
|
162
|
+
html+='<div class="card-description">'+item.description+'</div>';
|
|
163
|
+
}
|
|
164
|
+
body.innerHTML=html||'<div class="card-description" style="color:var(--text-secondary);">No facts available.</div>';
|
|
165
|
+
if(!currentItem||currentItem.name!==item.name){
|
|
166
|
+
chatHistory=[];
|
|
167
|
+
var msgArea=document.getElementById('detail-chat-messages');
|
|
168
|
+
msgArea.innerHTML='<div class="chat-welcome">Select the <strong>Ask Me</strong> tab to ask questions about <strong>'+item.name+'</strong>.</div>';
|
|
169
|
+
}
|
|
170
|
+
currentItem=item;
|
|
171
|
+
updateSuggestedQuestions(item);
|
|
172
|
+
card.classList.add('visible');
|
|
173
|
+
if(backdrop)backdrop.classList.add('visible');
|
|
174
|
+
setActiveTab('facts');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function closeCard(){
|
|
178
|
+
var card=document.getElementById('detail-card');
|
|
179
|
+
var backdrop=document.getElementById('card-backdrop');
|
|
180
|
+
if(card)card.classList.remove('visible');
|
|
181
|
+
if(backdrop)backdrop.classList.remove('visible');
|
|
182
|
+
currentItem=null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function setActiveTab(tabName){
|
|
186
|
+
document.querySelectorAll('#detail-card .card-tab').forEach(function(t){
|
|
187
|
+
t.classList.toggle('active',t.getAttribute('data-tab')===tabName);
|
|
188
|
+
});
|
|
189
|
+
document.querySelectorAll('#detail-card .tab-pane').forEach(function(p){
|
|
190
|
+
if(tabName==='facts')p.classList.toggle('active',p.id==='facts-pane');
|
|
191
|
+
else p.classList.toggle('active',p.id==='chat-pane');
|
|
192
|
+
});
|
|
193
|
+
if(tabName==='chat'){
|
|
194
|
+
var input=document.getElementById('detail-chat-input');
|
|
195
|
+
if(input)setTimeout(function(){input.focus();},100);
|
|
196
|
+
scrollChatToBottom();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function addChatBubble(text,isUser){
|
|
201
|
+
var area=document.getElementById('detail-chat-messages');
|
|
202
|
+
var welcome=area.querySelector('.chat-welcome');
|
|
203
|
+
if(welcome)welcome.remove();
|
|
204
|
+
var bubble=document.createElement('div');
|
|
205
|
+
bubble.className='chat-bubble '+(isUser?'user-bubble':'ai-bubble');
|
|
206
|
+
bubble.textContent=text;
|
|
207
|
+
area.appendChild(bubble);
|
|
208
|
+
scrollChatToBottom();
|
|
209
|
+
return bubble;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function addLoadingBubble(){
|
|
213
|
+
var area=document.getElementById('detail-chat-messages');
|
|
214
|
+
var bubble=document.createElement('div');
|
|
215
|
+
bubble.className='chat-bubble ai-bubble loading';
|
|
216
|
+
bubble.id='detail-loading-bubble';
|
|
217
|
+
bubble.innerHTML='<div class="typing-dots"><span></span><span></span><span></span></div>';
|
|
218
|
+
area.appendChild(bubble);
|
|
219
|
+
scrollChatToBottom();
|
|
220
|
+
return bubble;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function removeLoadingBubble(){
|
|
224
|
+
var el=document.getElementById('detail-loading-bubble');
|
|
225
|
+
if(el)el.remove();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function scrollChatToBottom(){
|
|
229
|
+
var area=document.getElementById('detail-chat-messages');
|
|
230
|
+
if(area)area.scrollTop=area.scrollHeight;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function updateSuggestedQuestions(item){
|
|
234
|
+
var container=document.getElementById('detail-suggested-q');
|
|
235
|
+
var questions=(item.suggestedQuestions&&item.suggestedQuestions.length)
|
|
236
|
+
?item.suggestedQuestions
|
|
237
|
+
:['What was it like here?','Tell me more about this place.','Why was this important?'];
|
|
238
|
+
container.innerHTML='';
|
|
239
|
+
questions.forEach(function(q){
|
|
240
|
+
var btn=document.createElement('button');
|
|
241
|
+
btn.className='suggested-q';
|
|
242
|
+
btn.textContent=q;
|
|
243
|
+
btn.addEventListener('click',function(){sendChat(q);});
|
|
244
|
+
container.appendChild(btn);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ─── LLM INTEGRATION ─────────────────────────────────────────────────
|
|
249
|
+
* The system prompt below is generic and factual. Customize it to match
|
|
250
|
+
* the page's topic, audience, or persona as needed.
|
|
251
|
+
* ──────────────────────────────────────────────────────────────────── */
|
|
252
|
+
function buildPrompt(userQuestion){
|
|
253
|
+
var parts=[];
|
|
254
|
+
parts.push('You are a knowledgeable expert. Answer questions about '+currentItem.name+' based on the provided context.');
|
|
255
|
+
parts.push('');
|
|
256
|
+
parts.push('Location: '+currentItem.name);
|
|
257
|
+
if(currentItem.subtitle)parts.push('Summary: '+currentItem.subtitle);
|
|
258
|
+
if(currentItem.description)parts.push('Description: '+currentItem.description);
|
|
259
|
+
if(currentItem.facts&¤tItem.facts.length){
|
|
260
|
+
parts.push('Key facts:');
|
|
261
|
+
currentItem.facts.forEach(function(f){parts.push('- '+f.label+': '+f.value);});
|
|
262
|
+
}
|
|
263
|
+
parts.push('');
|
|
264
|
+
parts.push('RULES:');
|
|
265
|
+
parts.push('- Keep answers concise (2-4 short paragraphs max)');
|
|
266
|
+
parts.push('- Be accurate and informative');
|
|
267
|
+
parts.push('- If you do not know something, say so honestly');
|
|
268
|
+
parts.push('');
|
|
269
|
+
if(chatHistory.length>0){
|
|
270
|
+
parts.push('Recent conversation:');
|
|
271
|
+
var recent=chatHistory.slice(-10);
|
|
272
|
+
recent.forEach(function(turn){
|
|
273
|
+
parts.push((turn.role==='user'?'User: ':'Assistant: ')+turn.text);
|
|
274
|
+
});
|
|
275
|
+
parts.push('');
|
|
276
|
+
}
|
|
277
|
+
parts.push('User question: '+userQuestion);
|
|
278
|
+
return parts.join('\n');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function sendChat(message){
|
|
282
|
+
if(!message||!message.trim()||!currentItem)return;
|
|
283
|
+
var input=document.getElementById('detail-chat-input');
|
|
284
|
+
var sendBtn=document.getElementById('detail-chat-send');
|
|
285
|
+
message=message.trim();
|
|
286
|
+
input.value='';
|
|
287
|
+
sendBtn.disabled=true;
|
|
288
|
+
setActiveTab('chat');
|
|
289
|
+
addChatBubble(message,true);
|
|
290
|
+
chatHistory.push({role:'user',text:message});
|
|
291
|
+
addLoadingBubble();
|
|
292
|
+
try{
|
|
293
|
+
var prompt=buildPrompt(message);
|
|
294
|
+
var result=await synthos.generate.completion({prompt:prompt,temperature:0.7});
|
|
295
|
+
removeLoadingBubble();
|
|
296
|
+
var answer=result.answer||'I wasn\'t able to generate a response. Please try again.';
|
|
297
|
+
addChatBubble(answer,false);
|
|
298
|
+
chatHistory.push({role:'assistant',text:answer});
|
|
299
|
+
if(chatHistory.length>20)chatHistory=chatHistory.slice(-20);
|
|
300
|
+
}catch(err){
|
|
301
|
+
removeLoadingBubble();
|
|
302
|
+
addChatBubble('Something went wrong. Please try again in a moment.',false);
|
|
303
|
+
console.error('Detail card chat error:',err);
|
|
304
|
+
}
|
|
305
|
+
sendBtn.disabled=false;
|
|
306
|
+
input.focus();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* ─── EVENT LISTENERS ─────────────────────────────────────────────── */
|
|
310
|
+
document.getElementById('card-close-btn').addEventListener('click',function(e){e.stopPropagation();closeCard();});
|
|
311
|
+
document.getElementById('card-backdrop').addEventListener('click',function(e){e.stopPropagation();closeCard();});
|
|
312
|
+
document.querySelectorAll('#detail-card .card-tab').forEach(function(tab){
|
|
313
|
+
tab.addEventListener('click',function(e){e.stopPropagation();setActiveTab(tab.getAttribute('data-tab'));});
|
|
314
|
+
});
|
|
315
|
+
document.getElementById('detail-chat-send').addEventListener('click',function(e){
|
|
316
|
+
e.stopPropagation();sendChat(document.getElementById('detail-chat-input').value);
|
|
317
|
+
});
|
|
318
|
+
document.getElementById('detail-chat-input').addEventListener('keydown',function(e){
|
|
319
|
+
if(e.key==='Enter'){e.preventDefault();e.stopPropagation();sendChat(this.value);}
|
|
320
|
+
});
|
|
321
|
+
document.getElementById('detail-chat-input').addEventListener('click',function(e){e.stopPropagation();});
|
|
322
|
+
|
|
323
|
+
/* ─── INIT ────────────────────────────────────────────────────────── */
|
|
324
|
+
waitForMap();
|
|
325
|
+
})();</script><script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script><script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script></body></html>
|