Release 0.7.13-alpha
This commit is contained in:
commit
73fd191a28
|
|
@ -1,3 +1,12 @@
|
|||
# 0.7.12-alpha:
|
||||
New features:
|
||||
- Add "New Window" command
|
||||
- Enable files to be opened with Lightningbeam
|
||||
Bugfixes:
|
||||
- Fix error when an object is deleted from a frame
|
||||
- Fix parent references being lost
|
||||
- Fix objects not showing up when imported multiple times
|
||||
|
||||
# 0.7.11-alpha:
|
||||
Bugfixes:
|
||||
- Fix duplicate objects showing up after grouping
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Ensure the script stops on error
|
||||
set -e
|
||||
|
||||
# Check if a version argument was passed
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: ./create-release.sh <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
RELEASE_BRANCH="release"
|
||||
MAIN_BRANCH="main"
|
||||
CONFIG_FILE="src-tauri/tauri.conf.json"
|
||||
|
||||
echo "Updating version to $VERSION in $CONFIG_FILE..."
|
||||
jq --arg version "$VERSION" '.version = $version' $CONFIG_FILE > tmp.json && mv tmp.json $CONFIG_FILE
|
||||
|
||||
echo "Committing to main..."
|
||||
git add $CONFIG_FILE
|
||||
git commit -m "Bump version to $VERSION"
|
||||
|
||||
echo "Checking out the release branch..."
|
||||
git checkout $RELEASE_BRANCH
|
||||
|
||||
echo "Merging the main branch into $RELEASE_BRANCH..."
|
||||
git merge $MAIN_BRANCH --no-ff -m "Release $VERSION"
|
||||
|
||||
echo "Pushing changes to the release branch..."
|
||||
git push origin $RELEASE_BRANCH
|
||||
|
||||
git checkout $MAIN_BRANCH
|
||||
|
||||
echo "Release $VERSION created and pushed successfully!"
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,7 +18,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri = { version = "2", features = ["protocol-asset"] }
|
||||
tauri-plugin-shell = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::{path::PathBuf, sync::Mutex};
|
||||
|
||||
use tauri_plugin_log::{Target, TargetKind};
|
||||
use log::{trace, info, debug, warn, error};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use chrono::Local;
|
||||
use tauri::{Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
use tauri::{AppHandle, Manager, Url, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -39,8 +39,10 @@ fn error(msg: String) {
|
|||
error!("{}",msg);
|
||||
}
|
||||
|
||||
use tauri::PhysicalSize;
|
||||
|
||||
#[tauri::command]
|
||||
async fn create_window(app: tauri::AppHandle) {
|
||||
async fn create_window(app: tauri::AppHandle, path: Option<String>) {
|
||||
let state = app.state::<Mutex<AppState>>();
|
||||
|
||||
// Lock the mutex to get mutable access:
|
||||
|
|
@ -51,15 +53,117 @@ async fn create_window(app: tauri::AppHandle) {
|
|||
state.counter += 1;
|
||||
|
||||
// Build the new window with the unique label
|
||||
let _webview_window = WebviewWindowBuilder::new(&app, &window_label, WebviewUrl::App("index.html".into()))
|
||||
let webview_window = WebviewWindowBuilder::new(&app, &window_label, WebviewUrl::App("index.html".into()))
|
||||
.title("Lightningbeam")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Get the current monitor's screen size from the new window
|
||||
if let Ok(Some(monitor)) = webview_window.current_monitor() {
|
||||
let screen_size = monitor.size(); // Get the size of the monitor
|
||||
let width = 4096;
|
||||
let height = 4096;
|
||||
|
||||
// Set the window size to be the smaller of the specified size or the screen size
|
||||
let new_width = width.min(screen_size.width as u32);
|
||||
let new_height = height.min(screen_size.height as u32 - 100);
|
||||
|
||||
// Set the size using PhysicalSize
|
||||
webview_window.set_size(tauri::Size::Physical(PhysicalSize::new(new_width, new_height)))
|
||||
.expect("Failed to set window size");
|
||||
} else {
|
||||
eprintln!("Could not detect the current monitor.");
|
||||
}
|
||||
|
||||
// Set the opened file if provided
|
||||
if let Some(val) = path {
|
||||
// Pass path data to the window via JavaScript
|
||||
webview_window.eval(&format!("window.openedFiles = [\"{val}\"]")).unwrap();
|
||||
|
||||
// Set the window title if provided
|
||||
webview_window.set_title(&val).expect("Failed to set window title");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn handle_file_associations(app: AppHandle, files: Vec<PathBuf>) {
|
||||
// -- Scope handling start --
|
||||
|
||||
// You can remove this block if you only want to know about the paths, but not actually "use" them in the frontend.
|
||||
|
||||
// This requires the `fs` tauri plugin and is required to make the plugin's frontend work:
|
||||
use tauri_plugin_fs::FsExt;
|
||||
let fs_scope = app.fs_scope();
|
||||
|
||||
// This is for the `asset:` protocol to work:
|
||||
let asset_protocol_scope = app.asset_protocol_scope();
|
||||
|
||||
for file in &files {
|
||||
// This requires the `fs` plugin:
|
||||
let _ = fs_scope.allow_file(file);
|
||||
|
||||
// This is for the `asset:` protocol:
|
||||
let _ = asset_protocol_scope.allow_file(file);
|
||||
}
|
||||
|
||||
// -- Scope handling end --
|
||||
|
||||
let files = files
|
||||
.into_iter()
|
||||
.map(|f| {
|
||||
let file = f.to_string_lossy().replace('\\', "\\\\"); // escape backslash
|
||||
format!("\"{file}\"",) // wrap in quotes for JS array
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
warn!("{}",files);
|
||||
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
window.eval(&format!("window.openedFiles = [{files}]")).unwrap();
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let pkg_name = env!("CARGO_PKG_NAME").to_string();
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
{
|
||||
app.manage(Mutex::new(AppState::default()));
|
||||
}
|
||||
#[cfg(any(windows, target_os = "linux"))] // Windows/Linux needs different handling from macOS
|
||||
{
|
||||
let mut files = Vec::new();
|
||||
|
||||
// NOTICE: `args` may include URL protocol (`your-app-protocol://`)
|
||||
// or arguments (`--`) if your app supports them.
|
||||
// files may aslo be passed as `file://path/to/file`
|
||||
for maybe_file in std::env::args().skip(1) {
|
||||
// skip flags like -f or --flag
|
||||
if maybe_file.starts_with('-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle `file://` path urls and skip other urls
|
||||
if let Ok(url) = Url::parse(&maybe_file) {
|
||||
// if let Ok(url) = url::Url::parse(&maybe_file) {
|
||||
if let Ok(path) = url.to_file_path() {
|
||||
files.push(path);
|
||||
}
|
||||
} else {
|
||||
files.push(PathBuf::from(maybe_file))
|
||||
}
|
||||
}
|
||||
|
||||
handle_file_associations(app.handle().clone(), files);
|
||||
}
|
||||
#[cfg(debug_assertions)] // only include this code on debug builds
|
||||
{
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
window.open_devtools();
|
||||
window.close_devtools();
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.plugin(
|
||||
tauri_plugin_log::Builder::new()
|
||||
.timezone_strategy(tauri_plugin_log::TimezoneStrategy::UseLocal)
|
||||
|
|
@ -81,26 +185,28 @@ pub fn run() {
|
|||
Target::new(TargetKind::LogDir { file_name: Some("logs".to_string()) }),
|
||||
Target::new(TargetKind::Webview),
|
||||
])
|
||||
.build(),
|
||||
.build()
|
||||
)
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.invoke_handler(tauri::generate_handler![greet, trace, debug, info, warn, error, create_window])
|
||||
// .manage(window_counter)
|
||||
.setup(|app| {
|
||||
#[cfg(debug_assertions)] // only include this code on debug builds
|
||||
{
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
window.open_devtools();
|
||||
window.close_devtools();
|
||||
}
|
||||
{
|
||||
app.manage(Mutex::new(AppState::default()));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while running tauri application")
|
||||
.run(
|
||||
#[allow(unused_variables)]
|
||||
|app, event| {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
if let tauri::RunEvent::Opened { urls } = event {
|
||||
let files = urls
|
||||
.into_iter()
|
||||
.filter_map(|url| url.to_file_path().ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
handle_file_associations(app.clone(), files);
|
||||
}
|
||||
},
|
||||
);
|
||||
tracing_subscriber::fmt().with_env_filter(EnvFilter::new(format!("{}=trace", pkg_name))).init();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Lightningbeam",
|
||||
"version": "0.7.11-alpha",
|
||||
"version": "0.7.13-alpha",
|
||||
"identifier": "org.lightningbeam.core",
|
||||
"build": {
|
||||
"frontendDist": "../src"
|
||||
|
|
@ -17,7 +17,10 @@
|
|||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": null,
|
||||
"assetProtocol": {
|
||||
"enable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
|
@ -43,6 +46,14 @@
|
|||
"files": {},
|
||||
"release": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": [
|
||||
"beam"
|
||||
],
|
||||
"mimeType": "text/plain"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
74
src/main.js
74
src/main.js
|
|
@ -354,6 +354,7 @@ let config = {
|
|||
undo: "<mod>z",
|
||||
redo: "<mod>Z",
|
||||
new: "<mod>n",
|
||||
newWindow: "<mod>N",
|
||||
save: "<mod>s",
|
||||
saveAs: "<mod>S",
|
||||
open: "<mod>o",
|
||||
|
|
@ -3688,6 +3689,9 @@ class GraphicsObject extends Widget {
|
|||
graphicsObject.currentFrameNum = json.currentFrameNum;
|
||||
graphicsObject.currentLayer = json.currentLayer;
|
||||
graphicsObject.children = [];
|
||||
if (json.parent in pointerList) {
|
||||
graphicsObject.parent = pointerList[json.parent]
|
||||
}
|
||||
for (let layer of json.layers) {
|
||||
graphicsObject.layers.push(Layer.fromJSON(layer));
|
||||
}
|
||||
|
|
@ -3714,6 +3718,7 @@ class GraphicsObject extends Widget {
|
|||
json.currentFrameNum = this.currentFrameNum;
|
||||
json.currentLayer = this.currentLayer;
|
||||
json.layers = [];
|
||||
json.parent = this.parent?.idx
|
||||
for (let layer of this.layers) {
|
||||
json.layers.push(layer.toJSON(randomizeUuid));
|
||||
}
|
||||
|
|
@ -4369,6 +4374,10 @@ function decrementFrame() {
|
|||
updateUI();
|
||||
}
|
||||
|
||||
function newWindow(path) {
|
||||
invoke("create_window", {app: window.__TAURI__.app, path: path})
|
||||
}
|
||||
|
||||
function _newFile(width, height, fps) {
|
||||
root = new GraphicsObject("root");
|
||||
context.objectStack = [root];
|
||||
|
|
@ -4409,7 +4418,7 @@ async function _save(path) {
|
|||
// console.log(action.name);
|
||||
// }
|
||||
const fileData = {
|
||||
version: "1.7.6",
|
||||
version: "1.7.7",
|
||||
width: config.fileWidth,
|
||||
height: config.fileHeight,
|
||||
fps: config.framerate,
|
||||
|
|
@ -4583,6 +4592,19 @@ async function _open(path, returnJson = false) {
|
|||
undoStack.push(action);
|
||||
}
|
||||
} else {
|
||||
if (file.version < "1.7.7") {
|
||||
function setParentReferences(obj, parentIdx = null) {
|
||||
if (obj.type === "GraphicsObject") {
|
||||
obj.parent = parentIdx; // Set the parent property
|
||||
}
|
||||
|
||||
Object.values(obj).forEach(child => {
|
||||
if (typeof child === 'object' && child !== null) setParentReferences(child, obj.type === "GraphicsObject" ? obj.idx : parentIdx);
|
||||
})
|
||||
}
|
||||
setParentReferences(file.json)
|
||||
console.log(file.json)
|
||||
}
|
||||
if (file.version < "1.7.6") {
|
||||
function restoreLineColors(obj) {
|
||||
// Step 1: Create colorMapping dictionary
|
||||
|
|
@ -4773,23 +4795,27 @@ async function importFile() {
|
|||
function assignUUIDs(obj, existing) {
|
||||
const uuidCache = {}; // Cache to store UUIDs for existing values
|
||||
|
||||
function deepAssign(obj) {
|
||||
function replaceUuids(obj) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
// Recurse for nested objects
|
||||
deepAssign(value);
|
||||
replaceUuids(value);
|
||||
} else if (value in existing && key != "name") {
|
||||
// If the value is in the "existing" list, assign a UUID
|
||||
if (!uuidCache[value]) {
|
||||
uuidCache[value] = uuidv4(); // Store the generated UUID for the value
|
||||
uuidCache[value] = uuidv4();
|
||||
}
|
||||
obj[key] = uuidCache[value]; // Assign the UUID to the object property
|
||||
} else if (key in existing) {
|
||||
// If the value is in the "existing" list, assign a UUID
|
||||
if (!uuidCache[key]) {
|
||||
uuidCache[key] = uuidv4(); // Store the generated UUID for the value
|
||||
}
|
||||
obj[key] = uuidCache[key]; // Assign the UUID to the object property
|
||||
obj[key] = uuidCache[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function replaceReferences(obj) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (key in existing) {
|
||||
obj[uuidCache[key]] = obj[key];
|
||||
delete obj[key]
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
replaceReferences(value);
|
||||
} else if (value in uuidCache) {
|
||||
obj[key] = value
|
||||
}
|
||||
|
|
@ -4797,7 +4823,8 @@ async function importFile() {
|
|||
}
|
||||
|
||||
// Start the recursion with the provided object
|
||||
deepAssign(obj);
|
||||
replaceUuids(obj);
|
||||
replaceReferences(obj)
|
||||
|
||||
return obj; // Return the updated object
|
||||
}
|
||||
|
|
@ -5944,6 +5971,7 @@ function stage() {
|
|||
for (let child of context.selection) {
|
||||
if (!context.activeObject.currentFrame) continue;
|
||||
if (!context.activeObject.currentFrame.keys) continue;
|
||||
if (!child.idx in context.activeObject.currentFrame.keys) continue;
|
||||
context.activeObject.currentFrame.keys[child.idx].x +=
|
||||
mouse.x - context.lastMouse.x;
|
||||
context.activeObject.currentFrame.keys[child.idx].y +=
|
||||
|
|
@ -6389,6 +6417,7 @@ function timeline() {
|
|||
|
||||
const frame = layer.getFrame(timeline_cvs.clicked_frame);
|
||||
if (frame.exists) {
|
||||
console.log(frame.keys)
|
||||
if (!e.shiftKey) {
|
||||
// Check if the clicked frame is already in the selection
|
||||
const existingIndex = context.selectedFrames.findIndex(
|
||||
|
|
@ -6700,7 +6729,9 @@ function outliner(object = undefined) {
|
|||
async function startup() {
|
||||
await loadConfig();
|
||||
createNewFileDialog(_newFile, _open, config);
|
||||
showNewFileDialog(config);
|
||||
if (!window.openedFiles) {
|
||||
showNewFileDialog(config);
|
||||
}
|
||||
}
|
||||
|
||||
startup();
|
||||
|
|
@ -7881,6 +7912,12 @@ async function renderMenu() {
|
|||
action: newFile,
|
||||
accelerator: getShortcut("new"),
|
||||
},
|
||||
{
|
||||
text: "New Window",
|
||||
enabled: true,
|
||||
action: newWindow,
|
||||
accelerator: getShortcut("newWindow"),
|
||||
},
|
||||
{
|
||||
text: "Save",
|
||||
enabled: true,
|
||||
|
|
@ -8306,3 +8343,10 @@ function renderAll() {
|
|||
}
|
||||
|
||||
renderAll();
|
||||
|
||||
if (window.openedFiles?.length>0) {
|
||||
_open(window.openedFiles[0])
|
||||
for (let i=1; i<window.openedFiles.length; i++) {
|
||||
newWindow(window.openedFiles[i])
|
||||
}
|
||||
}
|
||||
|
|
@ -145,12 +145,14 @@ function createNewFileDialog(newFileCallback, openFileCallback, config) {
|
|||
}
|
||||
|
||||
function showNewFileDialog(config) {
|
||||
if (!overlay) return;
|
||||
overlay.style.display = 'block';
|
||||
newFileDialog.style.display = 'block';
|
||||
displayFiles(config.recentFiles); // Reload the recent files
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
if (!overlay) return;
|
||||
overlay.style.display = 'none';
|
||||
newFileDialog.style.display = 'none';
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue