feat: admin panel - globe & chat AI sections, brighter globe
This commit is contained in:
parent
4e67efe531
commit
672fcf3f37
6 changed files with 406 additions and 6 deletions
145
admin.html
145
admin.html
|
|
@ -84,6 +84,13 @@
|
||||||
<a href="javascript:void(0)" class="sidebar-link" data-section="links" onclick="event.preventDefault(); AdminApp.showSection('links')">
|
<a href="javascript:void(0)" class="sidebar-link" data-section="links" onclick="event.preventDefault(); AdminApp.showSection('links')">
|
||||||
<span class="sidebar-icon">↗</span> Links
|
<span class="sidebar-icon">↗</span> Links
|
||||||
</a>
|
</a>
|
||||||
|
<a href="javascript:void(0)" class="sidebar-link" data-section="globe" onclick="event.preventDefault(); AdminApp.showSection('globe')">
|
||||||
|
<span class="sidebar-icon">🌍</span> Globe
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" class="sidebar-link" data-section="chatai" onclick="event.preventDefault(); AdminApp.showSection('chatai')">
|
||||||
|
<span class="sidebar-icon">🤖</span> Chat AI
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<div class="sidebar-divider"></div>
|
<div class="sidebar-divider"></div>
|
||||||
<div class="sidebar-section-label">SYSTEM</div>
|
<div class="sidebar-section-label">SYSTEM</div>
|
||||||
|
|
@ -1036,6 +1043,144 @@
|
||||||
<div id="backupStatus" class="backup-status"></div>
|
<div id="backupStatus" class="backup-status"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -->
|
||||||
|
<!-- 15. GLOBE CONFIG -->
|
||||||
|
<!-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -->
|
||||||
|
<div id="section-globe" class="admin-section">
|
||||||
|
<h2 class="section-heading">🌍 GLOBE CONFIGURATION</h2>
|
||||||
|
|
||||||
|
<!-- Server Location -->
|
||||||
|
<h3 class="section-subheading">◈ SERVER LOCATION</h3>
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeServerLat">LATITUDE</label>
|
||||||
|
<input type="number" id="globeServerLat" class="editor-input" placeholder="53.4808" step="0.0001">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeServerLng">LONGITUDE</label>
|
||||||
|
<input type="number" id="globeServerLng" class="editor-input" placeholder="-2.2426" step="0.0001">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeServerLabel">SERVER LABEL</label>
|
||||||
|
<input type="text" id="globeServerLabel" class="editor-input" placeholder="MANCHESTER">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Globe Settings -->
|
||||||
|
<h3 class="section-subheading">⚙ GLOBE SETTINGS</h3>
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeRotationSpeed">ROTATION SPEED <span id="globeRotationSpeedVal" class="hud-val">0.3</span></label>
|
||||||
|
<input type="range" id="globeRotationSpeed" class="editor-input hud-range" min="0.1" max="2" step="0.1" value="0.3">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeHexOpacity">HEX POLYGON OPACITY <span id="globeHexOpacityVal" class="hud-val">0.55</span></label>
|
||||||
|
<input type="range" id="globeHexOpacity" class="editor-input hud-range" min="0.1" max="1" step="0.05" value="0.55">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeAtmosphereColor">ATMOSPHERE COLOUR</label>
|
||||||
|
<div class="color-picker-row">
|
||||||
|
<input type="color" id="globeAtmosphereColor" class="editor-input color-input" value="#00cc33">
|
||||||
|
<input type="text" id="globeAtmosphereColorHex" class="editor-input color-hex" value="#00cc33">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="globeAtmosphereAlt">ATMOSPHERE ALTITUDE <span id="globeAtmosphereAltVal" class="hud-val">0.2</span></label>
|
||||||
|
<input type="range" id="globeAtmosphereAlt" class="editor-input hud-range" min="0.05" max="0.5" step="0.05" value="0.2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Arc Cities -->
|
||||||
|
<h3 class="section-subheading">⟐ ARC CITIES</h3>
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="arcCityName">CITY NAME</label>
|
||||||
|
<input type="text" id="arcCityName" class="editor-input" placeholder="e.g. New York">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="arcCityLat">LATITUDE</label>
|
||||||
|
<input type="number" id="arcCityLat" class="editor-input" placeholder="40.7128" step="0.0001">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="arcCityLng">LONGITUDE</label>
|
||||||
|
<input type="number" id="arcCityLng" class="editor-input" placeholder="-74.006" step="0.0001">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="action-btn accent sm" onclick="AdminApp.addArcCity()">+ ADD CITY</button>
|
||||||
|
|
||||||
|
<div class="table-wrap" style="margin-top:1rem;">
|
||||||
|
<table class="admin-table" id="arcCitiesTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>CITY</th>
|
||||||
|
<th>LAT</th>
|
||||||
|
<th>LNG</th>
|
||||||
|
<th>ACTIONS</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="arcCitiesBody">
|
||||||
|
<!-- Populated by JS -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button class="action-btn accent" onclick="AdminApp.saveGlobe()">⬡ SAVE GLOBE</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -->
|
||||||
|
<!-- 16. CHAT AI CONFIG -->
|
||||||
|
<!-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -->
|
||||||
|
<div id="section-chatai" class="admin-section">
|
||||||
|
<h2 class="section-heading">🤖 CHAT AI CONFIGURATION</h2>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="chatDisplayName">DISPLAY NAME</label>
|
||||||
|
<input type="text" id="chatDisplayName" class="editor-input" placeholder="JAE-AI">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="chatModel">MODEL NAME</label>
|
||||||
|
<input type="text" id="chatModel" class="editor-input" placeholder="venice-uncensored-1-2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="chatHeaderTag">HEADER TAG</label>
|
||||||
|
<input type="text" id="chatHeaderTag" class="editor-input" placeholder="VENICE-UNCENSORED">
|
||||||
|
</div>
|
||||||
|
<div class="editor-field">
|
||||||
|
<label class="editor-label" for="chatMaxHistory">MAX HISTORY</label>
|
||||||
|
<input type="number" id="chatMaxHistory" class="editor-input" placeholder="20" min="1" max="100">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="editor-label">AUTO GREETING</label>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="chatAutoGreeting" checked>
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="editor-field full">
|
||||||
|
<label class="editor-label" for="chatSystemPrompt">SYSTEM PROMPT SUMMARY (read-only info — actual prompt is in app.py)</label>
|
||||||
|
<textarea id="chatSystemPrompt" class="editor-input editor-textarea-sm" placeholder="AI assistant for jaeswift.xyz..." readonly style="opacity:0.7;"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button class="action-btn accent" onclick="AdminApp.saveChatAI()">⬡ SAVE CHAT CONFIG</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div><!-- /main-content -->
|
</div><!-- /main-content -->
|
||||||
</div><!-- /adminApp -->
|
</div><!-- /adminApp -->
|
||||||
|
|
||||||
|
|
|
||||||
30
api/app.py
30
api/app.py
|
|
@ -737,6 +737,36 @@ def venice_chat():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# ─── Globe Config ────────────────────────────────────
|
||||||
|
@app.route('/api/globe')
|
||||||
|
def get_globe():
|
||||||
|
return jsonify(load_json('globe.json'))
|
||||||
|
|
||||||
|
@app.route('/api/globe', methods=['POST'])
|
||||||
|
@require_auth
|
||||||
|
def save_globe():
|
||||||
|
try:
|
||||||
|
d = request.get_json(force=True)
|
||||||
|
save_json('globe.json', d)
|
||||||
|
return jsonify(d)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# ─── Chat AI Config ──────────────────────────────────
|
||||||
|
@app.route('/api/chat-config')
|
||||||
|
def get_chat_config():
|
||||||
|
return jsonify(load_json('chat_config.json'))
|
||||||
|
|
||||||
|
@app.route('/api/chat-config', methods=['POST'])
|
||||||
|
@require_auth
|
||||||
|
def save_chat_config():
|
||||||
|
try:
|
||||||
|
d = request.get_json(force=True)
|
||||||
|
save_json('chat_config.json', d)
|
||||||
|
return jsonify(d)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
# ─── Backups ─────────────────────────────────────────
|
# ─── Backups ─────────────────────────────────────────
|
||||||
@app.route('/api/backups/posts')
|
@app.route('/api/backups/posts')
|
||||||
@require_auth
|
@require_auth
|
||||||
|
|
|
||||||
8
api/data/chat_config.json
Normal file
8
api/data/chat_config.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"display_name": "JAE-AI",
|
||||||
|
"model": "venice-uncensored-1-2",
|
||||||
|
"system_prompt_summary": "AI assistant for jaeswift.xyz, knows all site sections and services",
|
||||||
|
"max_history": 20,
|
||||||
|
"auto_greeting": true,
|
||||||
|
"header_tag": "VENICE-UNCENSORED"
|
||||||
|
}
|
||||||
20
api/data/globe.json
Normal file
20
api/data/globe.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"server_lat": 53.4808,
|
||||||
|
"server_lng": -2.2426,
|
||||||
|
"server_label": "MANCHESTER",
|
||||||
|
"rotation_speed": 0.3,
|
||||||
|
"hex_polygon_opacity": 0.55,
|
||||||
|
"hex_polygon_color": "rgba(0, 220, 50, 0.55)",
|
||||||
|
"atmosphere_color": "#00cc33",
|
||||||
|
"atmosphere_altitude": 0.2,
|
||||||
|
"arc_cities": [
|
||||||
|
{"name": "New York", "lat": 40.7128, "lng": -74.006},
|
||||||
|
{"name": "Tokyo", "lat": 35.6762, "lng": 139.6503},
|
||||||
|
{"name": "Paris", "lat": 48.8566, "lng": 2.3522},
|
||||||
|
{"name": "Sydney", "lat": -33.8688, "lng": 151.2093},
|
||||||
|
{"name": "Berlin", "lat": 52.52, "lng": 13.405},
|
||||||
|
{"name": "Mumbai", "lat": 19.076, "lng": 72.8777},
|
||||||
|
{"name": "São Paulo", "lat": -23.5505, "lng": -46.6333},
|
||||||
|
{"name": "Seoul", "lat": 37.5665, "lng": 126.978}
|
||||||
|
]
|
||||||
|
}
|
||||||
196
js/admin.js
196
js/admin.js
|
|
@ -11,6 +11,8 @@ const AdminApp = {
|
||||||
servicesData: [],
|
servicesData: [],
|
||||||
navData: [],
|
navData: [],
|
||||||
linksData: [],
|
linksData: [],
|
||||||
|
globeData: null,
|
||||||
|
chatAIData: null,
|
||||||
themeDefaults: {
|
themeDefaults: {
|
||||||
accent: '#d0d0d0',
|
accent: '#d0d0d0',
|
||||||
bg: '#111111',
|
bg: '#111111',
|
||||||
|
|
@ -119,6 +121,31 @@ const AdminApp = {
|
||||||
valFont.textContent = fontSlider.value;
|
valFont.textContent = fontSlider.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globe slider sync
|
||||||
|
['globeRotationSpeed', 'globeHexOpacity', 'globeAtmosphereAlt'].forEach(id => {
|
||||||
|
const slider = document.getElementById(id);
|
||||||
|
const valEl = document.getElementById(id + 'Val');
|
||||||
|
if (slider && valEl) {
|
||||||
|
slider.addEventListener('input', () => {
|
||||||
|
valEl.textContent = slider.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Globe atmosphere colour sync
|
||||||
|
const globeAtmoPicker = document.getElementById('globeAtmosphereColor');
|
||||||
|
const globeAtmoHex = document.getElementById('globeAtmosphereColorHex');
|
||||||
|
if (globeAtmoPicker && globeAtmoHex) {
|
||||||
|
globeAtmoPicker.addEventListener('input', () => {
|
||||||
|
globeAtmoHex.value = globeAtmoPicker.value;
|
||||||
|
});
|
||||||
|
globeAtmoHex.addEventListener('input', () => {
|
||||||
|
if (/^#[0-9a-fA-F]{6}$/.test(globeAtmoHex.value)) {
|
||||||
|
globeAtmoPicker.value = globeAtmoHex.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async login(e) {
|
async login(e) {
|
||||||
|
|
@ -218,7 +245,9 @@ const AdminApp = {
|
||||||
'section-theme': 'Theme',
|
'section-theme': 'Theme',
|
||||||
'section-seo': 'SEO',
|
'section-seo': 'SEO',
|
||||||
'section-contact': 'Contact',
|
'section-contact': 'Contact',
|
||||||
'section-backups': 'Backups'
|
'section-backups': 'Backups',
|
||||||
|
'section-globe': 'Globe',
|
||||||
|
'section-chatai': 'Chat AI'
|
||||||
};
|
};
|
||||||
const topTitle = document.getElementById('topbarTitle') || document.querySelector('.topbar-title');
|
const topTitle = document.getElementById('topbarTitle') || document.querySelector('.topbar-title');
|
||||||
if (topTitle) topTitle.textContent = titleMap[name] || name.replace('section-', '').toUpperCase();
|
if (topTitle) topTitle.textContent = titleMap[name] || name.replace('section-', '').toUpperCase();
|
||||||
|
|
@ -237,6 +266,8 @@ const AdminApp = {
|
||||||
case 'section-theme': this.loadTheme(); break;
|
case 'section-theme': this.loadTheme(); break;
|
||||||
case 'section-seo': this.loadSeo(); break;
|
case 'section-seo': this.loadSeo(); break;
|
||||||
case 'section-contact': this.loadContactSettings(); break;
|
case 'section-contact': this.loadContactSettings(); break;
|
||||||
|
case 'section-globe': this.loadGlobe(); break;
|
||||||
|
case 'section-chatai': this.loadChatAI(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close mobile sidebar
|
// Close mobile sidebar
|
||||||
|
|
@ -1541,6 +1572,169 @@ const AdminApp = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* ─────────────────── GLOBE ─────────────────── */
|
||||||
|
|
||||||
|
async loadGlobe() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(this.API + '/globe', { headers: this.authHeaders() });
|
||||||
|
if (!res.ok) throw new Error('Failed to load globe config');
|
||||||
|
const data = await res.json();
|
||||||
|
this.globeData = data;
|
||||||
|
|
||||||
|
// Server location
|
||||||
|
this.setVal('globeServerLat', data.server_lat);
|
||||||
|
this.setVal('globeServerLng', data.server_lng);
|
||||||
|
this.setVal('globeServerLabel', data.server_label);
|
||||||
|
|
||||||
|
// Globe settings
|
||||||
|
const rotSpeed = data.rotation_speed || 0.3;
|
||||||
|
this.setVal('globeRotationSpeed', rotSpeed);
|
||||||
|
const rotVal = document.getElementById('globeRotationSpeedVal');
|
||||||
|
if (rotVal) rotVal.textContent = rotSpeed;
|
||||||
|
|
||||||
|
const hexOp = data.hex_polygon_opacity || 0.55;
|
||||||
|
this.setVal('globeHexOpacity', hexOp);
|
||||||
|
const hexVal = document.getElementById('globeHexOpacityVal');
|
||||||
|
if (hexVal) hexVal.textContent = hexOp;
|
||||||
|
|
||||||
|
// Atmosphere
|
||||||
|
const atmoColor = data.atmosphere_color || '#00cc33';
|
||||||
|
this.setVal('globeAtmosphereColor', atmoColor);
|
||||||
|
this.setVal('globeAtmosphereColorHex', atmoColor);
|
||||||
|
|
||||||
|
const atmoAlt = data.atmosphere_altitude || 0.2;
|
||||||
|
this.setVal('globeAtmosphereAlt', atmoAlt);
|
||||||
|
const atmoVal = document.getElementById('globeAtmosphereAltVal');
|
||||||
|
if (atmoVal) atmoVal.textContent = atmoAlt;
|
||||||
|
|
||||||
|
// Arc cities table
|
||||||
|
this.renderArcCities(data.arc_cities || []);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Globe load error:', err);
|
||||||
|
this.notify('Failed to load globe config', 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
renderArcCities(cities) {
|
||||||
|
const tbody = document.getElementById('arcCitiesBody');
|
||||||
|
if (!tbody) return;
|
||||||
|
if (!cities || cities.length === 0) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center;color:#555;">No arc cities configured</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tbody.innerHTML = cities.map((c, i) => {
|
||||||
|
return `<tr>
|
||||||
|
<td>${this.escapeHtml(c.name || '')}</td>
|
||||||
|
<td>${c.lat || 0}</td>
|
||||||
|
<td>${c.lng || 0}</td>
|
||||||
|
<td><button class="btn-sm btn-delete" onclick="AdminApp.removeArcCity(${i})" title="Remove">✗</button></td>
|
||||||
|
</tr>`;
|
||||||
|
}).join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
addArcCity() {
|
||||||
|
const name = this.getVal('arcCityName');
|
||||||
|
const lat = parseFloat(this.getVal('arcCityLat'));
|
||||||
|
const lng = parseFloat(this.getVal('arcCityLng'));
|
||||||
|
|
||||||
|
if (!name || isNaN(lat) || isNaN(lng)) {
|
||||||
|
this.notify('City name, latitude and longitude are required', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.globeData) this.globeData = {};
|
||||||
|
if (!this.globeData.arc_cities) this.globeData.arc_cities = [];
|
||||||
|
|
||||||
|
this.globeData.arc_cities.push({ name, lat, lng });
|
||||||
|
this.renderArcCities(this.globeData.arc_cities);
|
||||||
|
|
||||||
|
// Clear inputs
|
||||||
|
this.setVal('arcCityName', '');
|
||||||
|
this.setVal('arcCityLat', '');
|
||||||
|
this.setVal('arcCityLng', '');
|
||||||
|
this.notify('City added — save to persist', 'info');
|
||||||
|
},
|
||||||
|
|
||||||
|
removeArcCity(index) {
|
||||||
|
if (!this.globeData || !this.globeData.arc_cities) return;
|
||||||
|
if (!confirm('Remove this city?')) return;
|
||||||
|
this.globeData.arc_cities.splice(index, 1);
|
||||||
|
this.renderArcCities(this.globeData.arc_cities);
|
||||||
|
this.notify('City removed — save to persist', 'info');
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveGlobe() {
|
||||||
|
const payload = {
|
||||||
|
server_lat: parseFloat(this.getVal('globeServerLat')) || 53.4808,
|
||||||
|
server_lng: parseFloat(this.getVal('globeServerLng')) || -2.2426,
|
||||||
|
server_label: this.getVal('globeServerLabel') || 'MANCHESTER',
|
||||||
|
rotation_speed: parseFloat(this.getVal('globeRotationSpeed')) || 0.3,
|
||||||
|
hex_polygon_opacity: parseFloat(this.getVal('globeHexOpacity')) || 0.55,
|
||||||
|
hex_polygon_color: (this.globeData && this.globeData.hex_polygon_color) || 'rgba(0, 220, 50, 0.55)',
|
||||||
|
atmosphere_color: this.getVal('globeAtmosphereColorHex') || this.getVal('globeAtmosphereColor') || '#00cc33',
|
||||||
|
atmosphere_altitude: parseFloat(this.getVal('globeAtmosphereAlt')) || 0.2,
|
||||||
|
arc_cities: (this.globeData && this.globeData.arc_cities) || []
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(this.API + '/globe', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.authHeaders(),
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Save failed');
|
||||||
|
this.notify('Globe config saved');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Save globe error:', err);
|
||||||
|
this.notify('Failed to save globe config', 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ─────────────────── CHAT AI ─────────────────── */
|
||||||
|
|
||||||
|
async loadChatAI() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(this.API + '/chat-config', { headers: this.authHeaders() });
|
||||||
|
if (!res.ok) throw new Error('Failed to load chat config');
|
||||||
|
const data = await res.json();
|
||||||
|
this.chatAIData = data;
|
||||||
|
|
||||||
|
this.setVal('chatDisplayName', data.display_name);
|
||||||
|
this.setVal('chatModel', data.model);
|
||||||
|
this.setVal('chatHeaderTag', data.header_tag);
|
||||||
|
this.setVal('chatMaxHistory', data.max_history);
|
||||||
|
this.setChecked('chatAutoGreeting', data.auto_greeting !== false);
|
||||||
|
this.setVal('chatSystemPrompt', data.system_prompt_summary);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Chat AI load error:', err);
|
||||||
|
this.notify('Failed to load chat config', 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveChatAI() {
|
||||||
|
const payload = {
|
||||||
|
display_name: this.getVal('chatDisplayName') || 'JAE-AI',
|
||||||
|
model: this.getVal('chatModel') || 'venice-uncensored-1-2',
|
||||||
|
header_tag: this.getVal('chatHeaderTag') || 'VENICE-UNCENSORED',
|
||||||
|
max_history: parseInt(this.getVal('chatMaxHistory')) || 20,
|
||||||
|
auto_greeting: this.getChecked('chatAutoGreeting'),
|
||||||
|
system_prompt_summary: this.getVal('chatSystemPrompt') || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(this.API + '/chat-config', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.authHeaders(),
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Save failed');
|
||||||
|
this.notify('Chat AI config saved');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Save chat AI error:', err);
|
||||||
|
this.notify('Failed to save chat config', 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/* ─────────────────── UTILITIES ─────────────────── */
|
/* ─────────────────── UTILITIES ─────────────────── */
|
||||||
|
|
||||||
escapeHtml(str) {
|
escapeHtml(str) {
|
||||||
|
|
|
||||||
13
js/globe.js
13
js/globe.js
|
|
@ -29,7 +29,7 @@
|
||||||
.width(size)
|
.width(size)
|
||||||
.height(size)
|
.height(size)
|
||||||
.showAtmosphere(true)
|
.showAtmosphere(true)
|
||||||
.atmosphereColor('#00cc33')
|
.atmosphereColor('#00ff44')
|
||||||
.atmosphereAltitude(0.2)
|
.atmosphereAltitude(0.2)
|
||||||
// Hex polygons for land masses
|
// Hex polygons for land masses
|
||||||
.hexPolygonsData([])
|
.hexPolygonsData([])
|
||||||
|
|
@ -79,8 +79,8 @@
|
||||||
});
|
});
|
||||||
if (globeMesh && globeMesh.material) {
|
if (globeMesh && globeMesh.material) {
|
||||||
globeMesh.material.color.setHex(0x0a1a0a);
|
globeMesh.material.color.setHex(0x0a1a0a);
|
||||||
globeMesh.material.emissive.setHex(0x006600);
|
globeMesh.material.emissive.setHex(0x008800);
|
||||||
globeMesh.material.emissiveIntensity = 0.6;
|
globeMesh.material.emissiveIntensity = 0.8;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
(container);
|
(container);
|
||||||
|
|
@ -106,7 +106,10 @@
|
||||||
.hexPolygonResolution(3)
|
.hexPolygonResolution(3)
|
||||||
.hexPolygonMargin(0.4)
|
.hexPolygonMargin(0.4)
|
||||||
.hexPolygonColor(function () {
|
.hexPolygonColor(function () {
|
||||||
return 'rgba(0, 204, 51, 0.45)';
|
return 'rgba(0, 220, 50, 0.55)';
|
||||||
|
})
|
||||||
|
.hexPolygonSideColor(function () {
|
||||||
|
return 'rgba(0, 255, 60, 0.6)';
|
||||||
})
|
})
|
||||||
.hexPolygonAltitude(0.005);
|
.hexPolygonAltitude(0.005);
|
||||||
})
|
})
|
||||||
|
|
@ -140,7 +143,7 @@
|
||||||
startLng: city.lng,
|
startLng: city.lng,
|
||||||
endLat: SERVER_LAT,
|
endLat: SERVER_LAT,
|
||||||
endLng: SERVER_LNG,
|
endLng: SERVER_LNG,
|
||||||
color: ['rgba(0, 204, 51, 0.6)', 'rgba(0, 204, 51, 0.05)']
|
color: ['rgba(0, 255, 60, 0.7)', 'rgba(0, 255, 60, 0.08)']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arcs;
|
return arcs;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue