mirror of
https://github.com/sotam0316/drawNET.git
synced 2026-04-25 03:58:37 +09:00
142 lines
6.8 KiB
JavaScript
142 lines
6.8 KiB
JavaScript
import { FloatingWindow } from './ui/window.js';
|
|
import { logger } from './utils/logger.js';
|
|
|
|
class ObjectStudioPlugin {
|
|
constructor() {
|
|
this.win = null;
|
|
this.uploadedPath = '';
|
|
}
|
|
|
|
open() {
|
|
if (this.win) {
|
|
this.win.bringToFront();
|
|
return;
|
|
}
|
|
|
|
this.win = new FloatingWindow('studio', t('object_studio'), {
|
|
width: 700,
|
|
height: 500,
|
|
icon: 'fas fa-magic'
|
|
});
|
|
|
|
const content = `
|
|
<div class="studio-container" style="display: flex; gap: 30px; height: 100%;">
|
|
<!-- Preview Area -->
|
|
<div class="preview-area" style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; background: var(--item-bg); border-radius: 20px; border: 2px dashed var(--panel-border);">
|
|
<div id="studio-preview-icon" style="width: 120px; height: 120px; display: flex; align-items: center; justify-content: center;">
|
|
<i class="fas fa-image" style="font-size: 50px; color: var(--sub-text);"></i>
|
|
</div>
|
|
<p style="font-size: 11px; color: var(--sub-text); margin-top: 15px;">${t('preview')}</p>
|
|
</div>
|
|
|
|
<!-- Form Area -->
|
|
<div class="form-area" style="flex: 1.5; display: flex; flex-direction: column; justify-content: space-between;">
|
|
<div class="settings-group">
|
|
<h3 style="margin-bottom: 20px; font-size: 14px; border-bottom: 1px solid var(--panel-border); padding-bottom: 8px;">${t('basic_info')}</h3>
|
|
<div class="setting-item">
|
|
<span class="setting-label">${t('id_label')}</span>
|
|
<input type="text" id="studio-id" placeholder="${t('id_placeholder')}" style="width: 200px;">
|
|
</div>
|
|
<div class="setting-item">
|
|
<span class="setting-label">${t('display_name')}</span>
|
|
<input type="text" id="studio-label" placeholder="${t('name_placeholder')}" style="width: 200px;">
|
|
</div>
|
|
<div class="setting-item">
|
|
<span class="setting-label">${t('category')}</span>
|
|
<select id="studio-category" style="width: 200px;">
|
|
<option value="Network">${t('Network')}</option>
|
|
<option value="Security">${t('Security')}</option>
|
|
<option value="Compute">${t('Compute')}</option>
|
|
<option value="External">${t('External')}</option>
|
|
<option value="Other">${t('Other')}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-group">
|
|
<h3 style="margin-bottom: 20px; font-size: 14px; border-bottom: 1px solid var(--panel-border); padding-bottom: 8px;">${t('asset_attachment')}</h3>
|
|
<div class="setting-item" style="flex-direction: column; align-items: flex-start; gap: 10px;">
|
|
<span class="setting-label">${t('icon_image')}</span>
|
|
<div style="display: flex; gap: 10px; width: 100%;">
|
|
<input type="file" id="studio-file" accept=".svg,.png,.jpg,.jpeg" style="font-size: 11px; flex: 1;">
|
|
<button id="studio-upload-btn" class="btn-secondary" style="padding: 5px 15px; font-size: 10px;">${t('upload') || 'Upload'}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="studio-footer" style="margin-top: 30px; display: flex; justify-content: flex-end; gap: 12px;">
|
|
<button id="studio-cancel" class="btn-secondary">${t('close_btn')}</button>
|
|
<button id="studio-save" class="btn-primary" style="padding: 10px 40px;">${t('save_device')}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.win.render(content);
|
|
this.initEventListeners();
|
|
}
|
|
|
|
initEventListeners() {
|
|
const el = this.win.element;
|
|
const fileInput = el.querySelector('#studio-file');
|
|
const uploadBtn = el.querySelector('#studio-upload-btn');
|
|
const saveBtn = el.querySelector('#studio-save');
|
|
const cancelBtn = el.querySelector('#studio-cancel');
|
|
|
|
cancelBtn.addEventListener('click', () => this.win.destroy());
|
|
this.win.onClose = () => { this.win = null; };
|
|
|
|
uploadBtn.addEventListener('click', () => this.handleUpload(fileInput));
|
|
saveBtn.addEventListener('click', () => this.handleSave());
|
|
}
|
|
|
|
async handleUpload(fileInput) {
|
|
const file = fileInput.files[0];
|
|
if (!file) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
try {
|
|
const res = await fetch('/api/studio/upload', { method: 'POST', body: formData });
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
this.uploadedPath = data.path;
|
|
const preview = this.win.element.querySelector('#studio-preview-icon');
|
|
preview.innerHTML = `<img src="/static/assets/${this.uploadedPath}" style="max-width: 100%; max-height: 100%; transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1);">`;
|
|
preview.querySelector('img').style.transform = 'scale(0.8)';
|
|
setTimeout(() => { preview.querySelector('img').style.transform = 'scale(1)'; }, 10);
|
|
}
|
|
} catch (err) { logger.critical(err); }
|
|
}
|
|
|
|
async handleSave() {
|
|
const id = document.getElementById('studio-id').value.trim();
|
|
const label = document.getElementById('studio-label').value.trim();
|
|
const category = document.getElementById('studio-category').value;
|
|
|
|
if (!id || !label || !this.uploadedPath) {
|
|
alert("Please fill all required fields.");
|
|
return;
|
|
}
|
|
|
|
const newAsset = { id, type: label, label, category, path: this.uploadedPath, format: this.uploadedPath.split('.').pop() };
|
|
|
|
try {
|
|
const res = await fetch('/api/studio/add', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(newAsset)
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
initAssets();
|
|
this.win.destroy();
|
|
}
|
|
} catch (err) { logger.critical(err); }
|
|
}
|
|
}
|
|
|
|
export const studioPlugin = new ObjectStudioPlugin();
|
|
export const openStudioModal = () => studioPlugin.open();
|