Templates #1
@ -2,12 +2,16 @@
|
|||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { addTemplateFile } from '$lib/file_utils';
|
import { addTemplateFile } from '$lib/file_utils';
|
||||||
|
import Popup from '$lib/components/Popup.svelte';
|
||||||
let dragging = $state(false);
|
let dragging = $state(false);
|
||||||
let hasBlend = $state(false);
|
let hasBlend = $state(false);
|
||||||
let dropzoneEl = $state(null);
|
let dropzoneEl = $state(null);
|
||||||
|
|
||||||
let { version } = $props();
|
let { version } = $props();
|
||||||
|
|
||||||
|
let successPopup;
|
||||||
|
let errorPopup;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const win = getCurrentWindow();
|
const win = getCurrentWindow();
|
||||||
let unlisten = () => {};
|
let unlisten = () => {};
|
||||||
@ -47,9 +51,14 @@
|
|||||||
const blendFiles = payload.paths.filter((p) => p.endsWith('.blend'));
|
const blendFiles = payload.paths.filter((p) => p.endsWith('.blend'));
|
||||||
if (blendFiles.length > 0) {
|
if (blendFiles.length > 0) {
|
||||||
console.log('Dropped Blender files:', blendFiles);
|
console.log('Dropped Blender files:', blendFiles);
|
||||||
blendFiles.forEach((file) => {
|
blendFiles.forEach(async (file) => {
|
||||||
if (version) {
|
if (version) {
|
||||||
addTemplateFile(version, file);
|
const result = await addTemplateFile(version, file);
|
||||||
|
if (result.success) {
|
||||||
|
successPopup.pop(result.message, 3000);
|
||||||
|
} else {
|
||||||
|
errorPopup.pop(result.message, 4000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -66,6 +75,9 @@
|
|||||||
|
|
||||||
<div class="dropzone" class:dragging bind:this={dropzoneEl}>Drop Blender Files</div>
|
<div class="dropzone" class:dragging bind:this={dropzoneEl}>Drop Blender Files</div>
|
||||||
|
|
||||||
|
<Popup bind:this={successPopup} type="confirm" />
|
||||||
|
<Popup bind:this={errorPopup} type="error" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dropzone {
|
.dropzone {
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import { fade, fly } from 'svelte/transition';
|
import { fade, fly } from 'svelte/transition';
|
||||||
|
|
||||||
let { content = '', type = 'confirm' } = $props();
|
let { type = 'confirm' } = $props();
|
||||||
let open = $state(false);
|
let open = $state(false);
|
||||||
|
let message = $state('');
|
||||||
|
|
||||||
export function pop(resetTimeout = 0) {
|
export function pop(msg = '', resetTimeout = 0) {
|
||||||
|
message = msg;
|
||||||
open = true;
|
open = true;
|
||||||
if (resetTimeout > 0) {
|
if (resetTimeout > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -17,7 +19,7 @@
|
|||||||
{#if open}
|
{#if open}
|
||||||
<div in:fly={{ duration: 200, y: 100 }} out:fade class="popup {type}">
|
<div in:fly={{ duration: 200, y: 100 }} out:fade class="popup {type}">
|
||||||
<div class="popup-content">
|
<div class="popup-content">
|
||||||
<p>{content}</p>
|
<p>{message}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@ -35,4 +37,7 @@
|
|||||||
.confirm {
|
.confirm {
|
||||||
background-color: var(--light-accent);
|
background-color: var(--light-accent);
|
||||||
}
|
}
|
||||||
|
.error {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
async function handleDownload() {
|
async function handleDownload() {
|
||||||
let result = await downloadBlenderVersion(selectedLink);
|
let result = await downloadBlenderVersion(selectedLink);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
popup?.pop(2000);
|
popup?.pop('Blender ' + selectedLink.version + ' has been installed', 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +29,7 @@
|
|||||||
let popup = $state();
|
let popup = $state();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Popup bind:this={popup} content={'Blender ' + selectedLink.version + ' has been installed'}
|
<Popup bind:this={popup}></Popup>
|
||||||
></Popup>
|
|
||||||
<div class="download" class:full-width={downloading || installed}>
|
<div class="download" class:full-width={downloading || installed}>
|
||||||
<div class="selectVersion {downloading}">
|
<div class="selectVersion {downloading}">
|
||||||
<select class:disabled={downloading && !installed} bind:value={linkIndex}>
|
<select class:disabled={downloading && !installed} bind:value={linkIndex}>
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
import { deleteTemplate } from '$lib/library';
|
import { deleteTemplate } from '$lib/library';
|
||||||
|
|
||||||
import { launchBlenderVersion } from '$lib/library';
|
import { launchBlenderVersion } from '$lib/library';
|
||||||
|
import { notify } from '$lib/notification.js';
|
||||||
let { template, version } = $props();
|
let { template, version } = $props();
|
||||||
let templateName = $derived(
|
let templateName = $derived(
|
||||||
template
|
template
|
||||||
@ -22,13 +23,18 @@
|
|||||||
*/
|
*/
|
||||||
async function handleDelete(template) {
|
async function handleDelete(template) {
|
||||||
const prompt = await show({
|
const prompt = await show({
|
||||||
title: 'Delete',
|
title: 'Delete Template',
|
||||||
message: `This will remove Blender ${version.version} and all associated files from your system.`,
|
message: `This will delete the "${templateName}" template.`,
|
||||||
buttons: ['Cancel', 'Continue'],
|
buttons: ['Cancel', 'Continue'],
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
});
|
});
|
||||||
if (prompt == 'Continue') {
|
if (prompt == 'Continue') {
|
||||||
deleteTemplate(version, template);
|
const result = await deleteTemplate(version, template);
|
||||||
|
if (result.success) {
|
||||||
|
notify(result.message, 3000);
|
||||||
|
} else {
|
||||||
|
notify(result.message, 4000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
import Popup from '$lib/components/Popup.svelte';
|
|
||||||
import {
|
import {
|
||||||
deleteVersion,
|
deleteVersion,
|
||||||
launchBlenderVersion,
|
launchBlenderVersion,
|
||||||
@ -12,9 +11,9 @@
|
|||||||
import MenuItem from '$lib/components/MenuItem.svelte';
|
import MenuItem from '$lib/components/MenuItem.svelte';
|
||||||
|
|
||||||
import { show } from '$lib/components/Dialog.svelte';
|
import { show } from '$lib/components/Dialog.svelte';
|
||||||
|
import { notify } from '$lib/notification.js';
|
||||||
|
|
||||||
let { version, selectedVersion } = $props();
|
let { version, selectedVersion } = $props();
|
||||||
let popup = $state();
|
|
||||||
|
|
||||||
let selected = $derived(version.version === selectedVersion?.version);
|
let selected = $derived(version.version === selectedVersion?.version);
|
||||||
|
|
||||||
@ -26,7 +25,12 @@
|
|||||||
type: 'warning'
|
type: 'warning'
|
||||||
});
|
});
|
||||||
if (prompt == 'Continue') {
|
if (prompt == 'Continue') {
|
||||||
deleteVersion(version);
|
const result = await deleteVersion(version);
|
||||||
|
if (result.success) {
|
||||||
|
notify(result.message, 3000);
|
||||||
|
} else {
|
||||||
|
notify(result.message, 4000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,13 +42,11 @@
|
|||||||
type: 'info'
|
type: 'info'
|
||||||
});
|
});
|
||||||
if (prompt == 'Continue') {
|
if (prompt == 'Continue') {
|
||||||
popup.pop(1500);
|
notify('Version ' + version.version + ' is now the default on your system', 1500);
|
||||||
registerVersion(version);
|
registerVersion(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Popup bind:this={popup} content="Version {version.version} is now the default on your system" />
|
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
basename as pathBasename,
|
basename as pathBasename,
|
||||||
dirname as pathDirname
|
dirname as pathDirname
|
||||||
} from '@tauri-apps/api/path';
|
} from '@tauri-apps/api/path';
|
||||||
import { stat, mkdir, readDir, copyFile, rename, remove } from '@tauri-apps/plugin-fs';
|
import { stat, mkdir, readDir, copyFile, rename, remove, exists } from '@tauri-apps/plugin-fs';
|
||||||
import { open } from '@tauri-apps/plugin-dialog';
|
import { open } from '@tauri-apps/plugin-dialog';
|
||||||
import baseicon from '$lib/assets/baseicon.png';
|
import baseicon from '$lib/assets/baseicon.png';
|
||||||
import europa from '$lib/assets/fonts/Europa-Mono-Medium.otf';
|
import europa from '$lib/assets/fonts/Europa-Mono-Medium.otf';
|
||||||
@ -124,19 +124,25 @@ export async function addTemplateFile(version, filePath) {
|
|||||||
const newFileName = await pathBasename(filePath);
|
const newFileName = await pathBasename(filePath);
|
||||||
const templateName = newFileName.replace(/\.blend$/i, '');
|
const templateName = newFileName.replace(/\.blend$/i, '');
|
||||||
const newTemplatePath = await join(await getTemplatesDir(version), templateName);
|
const newTemplatePath = await join(await getTemplatesDir(version), templateName);
|
||||||
await ensureDirectory(newTemplatePath)
|
|
||||||
|
// Check for duplicate template name
|
||||||
|
if (await exists(newTemplatePath)) {
|
||||||
|
return { success: false, message: `Template "${templateName}" already exists` };
|
||||||
|
}
|
||||||
|
|
||||||
const newTemplateFile = await join(newTemplatePath, 'startup.blend');
|
const newTemplateFile = await join(newTemplatePath, 'startup.blend');
|
||||||
|
await ensureDirectory(newTemplatePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await copyFile(filePath, newTemplateFile);
|
await copyFile(filePath, newTemplateFile);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error adding template file:', error);
|
console.error('Error adding template file:', error);
|
||||||
return;
|
return { success: false, message: `Failed to add template: ${error.message || error}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
await getInstalledVersions();
|
await getInstalledVersions();
|
||||||
|
|
||||||
console.log('adding ', filePath, ' to ', newTemplateFile);
|
return { success: true, message: `Template "${templateName}" added` };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -255,8 +255,10 @@ export async function deleteTemplate(version, template) {
|
|||||||
try {
|
try {
|
||||||
await removeTemplate(templatePath);
|
await removeTemplate(templatePath);
|
||||||
await getInstalledVersions();
|
await getInstalledVersions();
|
||||||
|
return { success: true, message: `Template "${template}" deleted` };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete template', error);
|
console.error('Failed to delete template', error);
|
||||||
|
return { success: false, message: `Failed to delete template: ${error.message || error}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,9 +284,10 @@ export async function deleteVersion(version) {
|
|||||||
installedVersionsStore.set(filteredVersions);
|
installedVersionsStore.set(filteredVersions);
|
||||||
|
|
||||||
console.log(`Successfully deleted version ${version.version}`);
|
console.log(`Successfully deleted version ${version.version}`);
|
||||||
|
return { success: true, message: `Blender ${version.version} deleted` };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to delete version ${version.version}:`, error);
|
console.error(`Failed to delete version ${version.version}:`, error);
|
||||||
throw error;
|
return { success: false, message: `Failed to delete Blender ${version.version}: ${error.message || error}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
src/lib/notification.js
Normal file
12
src/lib/notification.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const notification = writable(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification popup.
|
||||||
|
* @param {string} message - The message to display
|
||||||
|
* @param {number} [timeout=3000] - Auto-dismiss timeout in milliseconds
|
||||||
|
*/
|
||||||
|
export function notify(message, timeout = 3000) {
|
||||||
|
notification.set({ message, timeout });
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@
|
|||||||
import Popup from '$lib/components/Popup.svelte';
|
import Popup from '$lib/components/Popup.svelte';
|
||||||
import Dialog from '$lib/components/Dialog.svelte';
|
import Dialog from '$lib/components/Dialog.svelte';
|
||||||
import Splash from '$lib/components/Splash.svelte';
|
import Splash from '$lib/components/Splash.svelte';
|
||||||
|
import { notification } from '$lib/notification.js';
|
||||||
|
|
||||||
// states
|
// states
|
||||||
let fadeInSettings = { duration: 100 };
|
let fadeInSettings = { duration: 100 };
|
||||||
@ -27,10 +28,17 @@
|
|||||||
let installedVersions = $state([]);
|
let installedVersions = $state([]);
|
||||||
let downloadTasks = $state([]);
|
let downloadTasks = $state([]);
|
||||||
let settings = $state();
|
let settings = $state();
|
||||||
|
let notificationPopup;
|
||||||
let initialized = $state(false);
|
let initialized = $state(false);
|
||||||
let version = $state('');
|
let version = $state('');
|
||||||
let loadingState = $state('loading'); // load, update, initialized
|
let loadingState = $state('loading'); // load, update, initialized
|
||||||
|
|
||||||
|
notification.subscribe((n) => {
|
||||||
|
if (n) {
|
||||||
|
notificationPopup?.pop(n.message, n.timeout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function initStoresListeners() {
|
async function initStoresListeners() {
|
||||||
currentSettings.subscribe((value) => {
|
currentSettings.subscribe((value) => {
|
||||||
settings = value;
|
settings = value;
|
||||||
@ -68,7 +76,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Popup content="Are you sure you want to delete this file?" type="confirm" />
|
<Popup bind:this={notificationPopup} />
|
||||||
<Dialog />
|
<Dialog />
|
||||||
|
|
||||||
<div id="main">
|
<div id="main">
|
||||||
@ -194,7 +202,7 @@
|
|||||||
#tabcontent {
|
#tabcontent {
|
||||||
background-color: var(--light);
|
background-color: var(--light);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: calc(100vh - 5rem);
|
height: calc(100vh - 3rem);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user