create route file action

This commit is contained in:
vcoppe committed 2026-10-26 19:22:14 +01:00
1 parent 764d0b233a
commit 0f957fb1f2
60 files changed
+208 -82

No files matched your search

+12 -3
View File
@@ -148,21 +148,30 @@ pub fn redo<H: History>(engine: &mut Engine<H>) -> Outcome {
// Files // Files
/// Creates a file and selects it. With `lng` and `lat`, it starts with a track and a segment that /// Creates a file and selects it. With `route`, it starts with a route. With `lng` and `lat`, it
/// hold that trackpoint (at `ele`, 0 by default). /// starts with a segment that holds that trackpoint (at `ele`, 0 by default): the one of its route
/// with `route`, of a track otherwise.
pub fn new_file<H: History>( pub fn new_file<H: History>(
engine: &mut Engine<H>, engine: &mut Engine<H>,
name: &str, name: &str,
lng: Option<f64>, lng: Option<f64>,
lat: Option<f64>, lat: Option<f64>,
ele: Option<f64>, ele: Option<f64>,
route: bool,
) -> Outcome { ) -> Outcome {
let trackpoint = lng.zip(lat).map(|(lng, lat)| engine::NewTrackpoint { let trackpoint = lng.zip(lat).map(|(lng, lat)| engine::NewTrackpoint {
lng, lng,
lat, lat,
ele: ele.unwrap_or_default(), ele: ele.unwrap_or_default(),
}); });
edit(engine, Command::New(engine::New { name, trackpoint })) edit(
engine,
Command::New(engine::New {
name,
trackpoint,
route,
}),
)
} }
/// Loads files as a single command (one undo step). The files are in `data`, one after the other, /// Loads files as a single command (one undo step). The files are in `data`, one after the other,
@@ -59,12 +59,14 @@ mod tests {
New { New {
name: "first", name: "first",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
New { New {
name: "second", name: "second",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); // now selected .unwrap(); // now selected
@@ -122,6 +124,7 @@ mod tests {
New { New {
name: "file", name: "file",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -36,6 +36,7 @@ mod tests {
New { New {
name: "file", name: "file",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -61,6 +61,7 @@ mod tests {
New { New {
name: "file", name: "file",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -77,6 +78,7 @@ mod tests {
New { New {
name: "file", name: "file",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -29,6 +29,7 @@ mod tests {
New { New {
name: "file", name: "file",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
+2
View File
@@ -146,6 +146,7 @@ mod tests {
crate::New { crate::New {
name: "keep", name: "keep",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -280,6 +281,7 @@ mod tests {
crate::New { crate::New {
name: "keep", name: "keep",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -31,12 +31,14 @@ mod tests {
crate::New { crate::New {
name: "a", name: "a",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
crate::New { crate::New {
name: "b", name: "b",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -217,6 +217,7 @@ mod tests {
crate::New { crate::New {
name, name,
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
+3
View File
@@ -85,6 +85,7 @@ mod tests {
crate::New { crate::New {
name: "keep", name: "keep",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -140,6 +141,7 @@ mod tests {
crate::New { crate::New {
name: "before", name: "before",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -189,6 +191,7 @@ mod tests {
crate::New { crate::New {
name: "keep", name: "keep",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
+53 -6
View File
@@ -1,12 +1,16 @@
use crate::{Apply, CommandError, File, LngLat, State, Track, TrackSegment, Trackpoint, produce}; use crate::{
Apply, CommandError, File, LngLat, Route, State, Track, TrackSegment, Trackpoint, produce,
};
/// Creates an empty file and selects it. With a `trackpoint`, the file starts with a track and a /// Creates an empty file and selects it. With `route`, the file starts with a route, which is
/// segment that hold it: it is how the routing tool starts a route on an empty map, in a single /// empty. With a `trackpoint`, the file starts with a segment that holds it, the one of its route
/// undo step. /// with `route` or of a track otherwise: it is how the routing tool starts a route on an empty map,
/// in a single undo step.
#[derive(Debug)] #[derive(Debug)]
pub struct New<'a> { pub struct New<'a> {
pub name: &'a str, pub name: &'a str,
pub trackpoint: Option<NewTrackpoint>, pub trackpoint: Option<NewTrackpoint>,
pub route: bool,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@@ -21,8 +25,8 @@ impl Apply for New<'_> {
produce(state, |_| { produce(state, |_| {
let mut file = File::default(); let mut file = File::default();
file.info.name = self.name.to_owned(); file.info.name = self.name.to_owned();
if let Some(point) = self.trackpoint {
let mut segment = TrackSegment::default(); let mut segment = TrackSegment::default();
if let Some(point) = self.trackpoint {
// the ends of a segment are anchors of the routing tool // the ends of a segment are anchors of the routing tool
segment.splice( segment.splice(
0, 0,
@@ -36,6 +40,13 @@ impl Apply for New<'_> {
..Default::default() ..Default::default()
}], }],
); );
}
if self.route {
file.rte.push(Route {
seg: segment,
..Default::default()
});
} else if self.trackpoint.is_some() {
file.trk.push(Track { file.trk.push(Track {
trkseg: vec![segment], trkseg: vec![segment],
..Default::default() ..Default::default()
@@ -59,7 +70,8 @@ mod tests {
assert!( assert!(
New { New {
name: "new", name: "new",
trackpoint: None trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.is_ok() .is_ok()
@@ -83,6 +95,7 @@ mod tests {
New { New {
name: "new", name: "new",
trackpoint: Some(point), trackpoint: Some(point),
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -98,4 +111,38 @@ mod tests {
assert_eq!(segment[0].anchor, Some(0)); assert_eq!(segment[0].anchor, Some(0));
assert_eq!(fx.selected_files(), [file.id].into()); assert_eq!(fx.selected_files(), [file.id].into());
} }
#[test]
fn test_new_with_a_route() {
let mut fx = Fixture::default();
New {
name: "new",
trackpoint: None,
route: true,
}
.apply(&mut fx.state())
.unwrap();
let file = fx.files.values().next().unwrap();
assert_eq!((file.rte.len(), file.trk.len()), (1, 0));
assert!(file.rte[0].seg.is_empty());
assert_eq!(fx.selected_files(), [file.id].into());
// the trackpoint goes to the route
let mut fx = Fixture::default();
New {
name: "new",
trackpoint: Some(NewTrackpoint {
lng: 1.0,
lat: 2.0,
ele: 3.0,
}),
route: true,
}
.apply(&mut fx.state())
.unwrap();
let file = fx.files.values().next().unwrap();
assert_eq!((file.rte.len(), file.trk.len()), (1, 0));
assert_eq!(file.rte[0].seg.len(), 1);
assert_eq!(file.rte[0].seg[0].anchor, Some(0));
}
} }
+1
View File
@@ -80,6 +80,7 @@ mod tests {
New { New {
name, name,
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -119,12 +119,14 @@ mod tests {
New { New {
name: "a", name: "a",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
New { New {
name: "b", name: "b",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -170,6 +172,7 @@ mod tests {
New { New {
name: "a", name: "a",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -197,6 +200,7 @@ mod tests {
New { New {
name, name,
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -235,6 +239,7 @@ mod tests {
New { New {
name: "a", name: "a",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
@@ -251,6 +256,7 @@ mod tests {
New { New {
name: "a", name: "a",
trackpoint: None, trackpoint: None,
route: false,
} }
.apply(&mut fx.state()) .apply(&mut fx.state())
.unwrap(); .unwrap();
+2
View File
@@ -87,6 +87,7 @@ fn new(engine: &mut TestEngine, name: &str) -> bool {
Command::New(New { Command::New(New {
name, name,
trackpoint: None, trackpoint: None,
route: false,
}), }),
) )
} }
@@ -1489,6 +1490,7 @@ mod per_file_history {
.execute(Action::Edit(Command::New(New { .execute(Action::Edit(Command::New(New {
name, name,
trackpoint: None, trackpoint: None,
route: false,
}))) })))
.unwrap(); .unwrap();
*engine.order().last().unwrap() *engine.order().last().unwrap()
+7 -1
View File
@@ -16,10 +16,16 @@ pub fn new_file(
lng: Option<f64>, lng: Option<f64>,
lat: Option<f64>, lat: Option<f64>,
ele: Option<f64>, ele: Option<f64>,
route: Option<bool>,
folder: Option<PathBuf>, folder: Option<PathBuf>,
) -> api::Outcome { ) -> api::Outcome {
let route = route.unwrap_or_default();
// in `folder` if it is given, otherwise like the selected file, otherwise in the open folder // in `folder` if it is given, otherwise like the selected file, otherwise in the open folder
state.core(move |core| core.creating(folder, true, |e| api::new_file(e, &name, lng, lat, ele))) state.core(move |core| {
core.creating(folder, true, |e| {
api::new_file(e, &name, lng, lat, ele, route)
})
})
} }
/// The files are in the body, one after the other, the headers `lengths` and `names` (JSON, /// The files are in the body, one after the other, the headers `lengths` and `names` (JSON,
+1 -1
View File
@@ -483,7 +483,7 @@ fn files_made_from_a_file_of_the_open_folder_are_written_next_to_it_at_once() {
assert_eq!(opened.added().len(), 1); assert_eq!(opened.added().len(), 1);
let copy = core.creating(None, false, gpx_engine::api::duplicate); let copy = core.creating(None, false, gpx_engine::api::duplicate);
let fresh = core.creating(None, true, |e| { let fresh = core.creating(None, true, |e| {
gpx_engine::api::new_file(e, "fresh", None, None, None) gpx_engine::api::new_file(e, "fresh", None, None, None, false)
}); });
// a copy of the file goes next to it, a new file with nothing selected in the selection // a copy of the file goes next to it, a new file with nothing selected in the selection
// of the file that was just made // of the file that was just made
+6 -6
View File
@@ -1247,7 +1247,7 @@ mod tests {
#[test] #[test]
fn a_file_that_is_not_on_the_disk_is_unsaved_until_it_is_given_a_place() { fn a_file_that_is_not_on_the_disk_is_unsaved_until_it_is_given_a_place() {
let mut fx = Fixture::new(); let mut fx = Fixture::new();
api::new_file(&mut fx.engine, "fresh", None, None, None); api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let id = fx.ids()[0]; let id = fx.ids()[0];
assert!(fx.workspace.is_dirty(&fx.engine, id)); assert!(fx.workspace.is_dirty(&fx.engine, id));
assert_eq!( assert_eq!(
@@ -1272,9 +1272,9 @@ mod tests {
fn several_files_without_a_place_go_in_a_folder_with_names_that_are_free() { fn several_files_without_a_place_go_in_a_folder_with_names_that_are_free() {
let mut fx = Fixture::new(); let mut fx = Fixture::new();
fx.write("fresh.gpx", "already there"); fx.write("fresh.gpx", "already there");
api::new_file(&mut fx.engine, "fresh", None, None, None); api::new_file(&mut fx.engine, "fresh", None, None, None, false);
api::new_file(&mut fx.engine, "a/b: c", None, None, None); api::new_file(&mut fx.engine, "a/b: c", None, None, None, false);
api::new_file(&mut fx.engine, "fresh", None, None, None); api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let ids = fx.ids(); let ids = fx.ids();
let folder = fx.folder.clone(); let folder = fx.folder.clone();
fx.workspace fx.workspace
@@ -1697,7 +1697,7 @@ mod tests {
fx.open_folder(); fx.open_folder();
let a = fx.ids()[0]; let a = fx.ids()[0];
fx.rename(a, "edited"); fx.rename(a, "edited");
api::new_file(&mut fx.engine, "fresh", None, None, None); api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let fresh = fx.id_named("fresh"); let fresh = fx.id_named("fresh");
fx.workspace.close_files(&mut fx.engine, &[a, fresh], true); fx.workspace.close_files(&mut fx.engine, &[a, fresh], true);
assert!(fx.ids().is_empty()); assert!(fx.ids().is_empty());
@@ -1713,7 +1713,7 @@ mod tests {
let mut fx = Fixture::new(); let mut fx = Fixture::new();
fx.write("a.gpx", "a"); fx.write("a.gpx", "a");
fx.open_folder(); fx.open_folder();
api::new_file(&mut fx.engine, "fresh", None, None, None); api::new_file(&mut fx.engine, "fresh", None, None, None, false);
assert_eq!(fx.ids().len(), 2); assert_eq!(fx.ids().len(), 2);
// the new file has nowhere to go: it has to be discarded // the new file has nowhere to go: it has to be discarded
+11 -4
View File
@@ -10,11 +10,18 @@ use crate::ts::*;
// File commands // File commands
/// Creates a file and selects it. With `lng` and `lat`, it starts with a track and a segment that /// Creates a file and selects it. With `route`, it starts with a route. With `lng` and `lat`, it
/// hold that trackpoint (at `ele`, 0 by default). /// starts with a segment that holds that trackpoint (at `ele`, 0 by default): the one of its route
/// with `route`, of a track otherwise.
#[wasm_bindgen] #[wasm_bindgen]
pub fn new_file(name: &str, lng: Option<f64>, lat: Option<f64>, ele: Option<f64>) -> Outcome { pub fn new_file(
run(|e| api::new_file(e, name, lng, lat, ele)) name: &str,
lng: Option<f64>,
lat: Option<f64>,
ele: Option<f64>,
route: bool,
) -> Outcome {
run(|e| api::new_file(e, name, lng, lat, ele, route))
} }
/// Loads files as a single command (one undo step). The files are in `data`, one after the /// Loads files as a single command (one undo step). The files are in `data`, one after the
+17 -7
View File
@@ -84,7 +84,7 @@ fn uuid_bytes(ids: &[String]) -> Vec<u8> {
fn test_new_file_is_listed_and_selected() { fn test_new_file_is_listed_and_selected() {
start(); start();
assert!(run(crate::command::files::new_file( assert!(run(crate::command::files::new_file(
"route", None, None, None "route", None, None, None, false
))); )));
assert_eq!(last_error(), None); assert_eq!(last_error(), None);
@@ -198,7 +198,9 @@ fn test_the_reason_of_a_failure_can_be_read() {
assert!(!run(crate::command::tools::reverse())); assert!(!run(crate::command::tools::reverse()));
assert_eq!(last_error().as_deref(), Some("nothing to do")); assert_eq!(last_error().as_deref(), Some("nothing to do"));
// the next call starts afresh // the next call starts afresh
assert!(run(crate::command::files::new_file("a", None, None, None))); assert!(run(crate::command::files::new_file(
"a", None, None, None, false
)));
assert_eq!(last_error(), None); assert_eq!(last_error(), None);
// invalid data // invalid data
@@ -251,7 +253,9 @@ fn test_the_reason_of_a_failure_can_be_read() {
assert!(last_error().is_some()); assert!(last_error().is_some());
// answers are not failures // answers are not failures
assert!(run(crate::command::files::new_file("b", None, None, None))); assert!(run(crate::command::files::new_file(
"b", None, None, None, false
)));
assert!(!crate::command::files::can_paste()); assert!(!crate::command::files::can_paste());
assert_eq!(last_error(), None); assert_eq!(last_error(), None);
} }
@@ -260,7 +264,9 @@ fn test_the_reason_of_a_failure_can_be_read() {
fn test_undo_and_redo() { fn test_undo_and_redo() {
started_with(&[]); started_with(&[]);
assert!(!can_undo() && !can_redo()); assert!(!can_undo() && !can_redo());
assert!(run(crate::command::files::new_file("a", None, None, None))); assert!(run(crate::command::files::new_file(
"a", None, None, None, false
)));
assert!(can_undo() && !can_redo()); assert!(can_undo() && !can_redo());
assert!(run(undo())); assert!(run(undo()));
assert!(order().is_empty()); assert!(order().is_empty());
@@ -386,7 +392,7 @@ fn test_move_elements_takes_js_objects() {
fn test_waypoints() { fn test_waypoints() {
started_with(&[]); started_with(&[]);
assert!(run(crate::command::files::new_file( assert!(run(crate::command::files::new_file(
"file", None, None, None "file", None, None, None, false
))); )));
let file = order().remove(0); let file = order().remove(0);
assert!(run(crate::command::waypoints::new_waypoint( assert!(run(crate::command::waypoints::new_waypoint(
@@ -490,7 +496,9 @@ fn test_editing_tools_change_the_statistics() {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn test_last_update_describes_the_last_call_only() { fn test_last_update_describes_the_last_call_only() {
started_with(&[]); started_with(&[]);
assert!(run(crate::command::files::new_file("a", None, None, None))); assert!(run(crate::command::files::new_file(
"a", None, None, None, false
)));
let update = last_outcome(); let update = last_outcome();
assert_eq!(array(get(&update, "added")).length(), 1); assert_eq!(array(get(&update, "added")).length(), 1);
assert_eq!(get(&update, "orderChanged").as_bool(), Some(true)); assert_eq!(get(&update, "orderChanged").as_bool(), Some(true));
@@ -502,7 +510,9 @@ fn test_last_update_describes_the_last_call_only() {
assert_eq!(get(&update, "orderChanged").as_bool(), Some(false)); assert_eq!(get(&update, "orderChanged").as_bool(), Some(false));
assert_eq!(get(&update, "selectionChanged").as_bool(), Some(false)); assert_eq!(get(&update, "selectionChanged").as_bool(), Some(false));
assert!(run(crate::command::files::new_file("b", None, None, None))); assert!(run(crate::command::files::new_file(
"b", None, None, None, false
)));
assert!(!run(crate::command::tools::crop(3, 1))); assert!(!run(crate::command::tools::crop(3, 1)));
let update = last_outcome(); let update = last_outcome();
assert_eq!(array(get(&update, "added")).length(), 0); assert_eq!(array(get(&update, "added")).length(), 0);
+6
View File
@@ -20,6 +20,12 @@ export function createFile() {
currentTool.set(Tool.ROUTING); currentTool.set(Tool.ROUTING);
} }
/** Creates a file that starts with an empty route, which the routing tool then draws. */
export function createRouteFile() {
engine.newFile(newFileName(), undefined, true);
currentTool.set(Tool.ROUTING);
}
/** Asks for the files to open: with the file picker of the system in the desktop app. */ /** Asks for the files to open: with the file picker of the system in the desktop app. */
export async function openFiles() { export async function openFiles() {
if (get(engine.workspace)) { if (get(engine.workspace)) {
+6 -1
View File
@@ -68,6 +68,7 @@
closeFiles, closeFiles,
closeFolder, closeFolder,
createFile, createFile,
createRouteFile,
loadFiles, loadFiles,
openFiles, openFiles,
openFolder, openFolder,
@@ -160,9 +161,13 @@
<Menubar.Content class="border-none"> <Menubar.Content class="border-none">
<Menubar.Item onclick={createFile}> <Menubar.Item onclick={createFile}>
<Plus size="16" /> <Plus size="16" />
{i18n._('menu.new')} {i18n._('menu.new_file')}
<Shortcut key="+" ctrl={true} /> <Shortcut key="+" ctrl={true} />
</Menubar.Item> </Menubar.Item>
<Menubar.Item onclick={createRouteFile}>
<Plus size="16" />
{i18n._('menu.new_route_file')}
</Menubar.Item>
<Menubar.Separator /> <Menubar.Separator />
<Menubar.Item onclick={openFiles}> <Menubar.Item onclick={openFiles}>
<FolderOpen size="16" /> <FolderOpen size="16" />
@@ -116,12 +116,16 @@
/** The folder where what is made from the menu goes: the folder of the row. */ /** The folder where what is made from the menu goes: the folder of the row. */
let folder = $derived(kind === 'file' ? undefined : path); let folder = $derived(kind === 'file' ? undefined : path);
/** Asks for the name of a file or a folder to make in the folder of the row (see `creating`). */ /**
function create(what: 'file' | 'folder') { * Asks for the name of a file or a folder to make in the folder of the row (see `creating`).
* With `route`, the file starts with an empty route.
*/
function create(what: 'file' | 'folder', route = false) {
if (folder === undefined) return; if (folder === undefined) return;
// where what is being named is shown // where what is being named is shown
folders.open(folder); folders.open(folder);
creating.kind = what; creating.kind = what;
creating.route = route;
creating.parent = folder; creating.parent = folder;
} }
@@ -286,6 +290,10 @@
<Plus size="16" /> <Plus size="16" />
{i18n._('menu.explorer_menu.new_file')} {i18n._('menu.explorer_menu.new_file')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item onclick={() => create('file', true)}>
<Plus size="16" />
{i18n._('menu.explorer_menu.new_route_file')}
</ContextMenu.Item>
<ContextMenu.Item onclick={() => create('folder')}> <ContextMenu.Item onclick={() => create('folder')}>
<Plus size="16" /> <Plus size="16" />
{i18n._('menu.explorer_menu.new_folder')} {i18n._('menu.explorer_menu.new_folder')}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { CollapsibleTreeNode } from '$lib/components/collapsible-tree/index'; import { CollapsibleTreeNode } from '$lib/components/collapsible-tree/index';
import { FileText, Folder } from '@lucide/svelte'; import { FileText, Folder, Route } from '@lucide/svelte';
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
@@ -45,6 +45,7 @@
async function create(value: string, retry: boolean) { async function create(value: string, retry: boolean) {
const kind = pending; const kind = pending;
const parent = folder; const parent = folder;
const route = creating.route;
if (kind === undefined || parent === undefined) return; if (kind === undefined || parent === undefined) return;
const name = value.trim(); const name = value.trim();
const taken = entries.some( const taken = entries.some(
@@ -60,7 +61,7 @@
if (name === '' || taken) return; if (name === '' || taken) return;
let created: string | undefined; let created: string | undefined;
if (kind === 'file') { if (kind === 'file') {
const id = await engine.newFileIn(name, parent); const id = await engine.newFileIn(name, parent, route);
created = id === undefined ? undefined : get(workspace)?.files[id]?.path; created = id === undefined ? undefined : get(workspace)?.files[id]?.path;
if (created !== undefined) { if (created !== undefined) {
currentTool.set(Tool.ROUTING); currentTool.set(Tool.ROUTING);
@@ -94,14 +95,20 @@
<!-- the name of what is being made, which it is made with --> <!-- the name of what is being made, which it is made with -->
{#snippet named(kind: 'file' | 'folder')} {#snippet named(kind: 'file' | 'folder')}
<div class="flex flex-row items-center h-7 pl-1 pr-1 {kind === 'file' ? 'gap-1.5' : 'gap-1'}"> <div class="flex flex-row items-center h-7 pl-1 pr-1 {kind === 'file' ? 'gap-1.5' : 'gap-1'}">
{#if kind === 'file'} {#if kind === 'file' && creating.route}
<Route size="16" class="shrink-0" />
{:else if kind === 'file'}
<FileText size="16" class="shrink-0" /> <FileText size="16" class="shrink-0" />
{:else} {:else}
<Folder size="16" class="shrink-0" /> <Folder size="16" class="shrink-0" />
{/if} {/if}
<NameInput <NameInput
placeholder={i18n._( placeholder={i18n._(
kind === 'file' ? 'menu.explorer_menu.new_file' : 'menu.explorer_menu.new_folder' kind === 'folder'
? 'menu.explorer_menu.new_folder'
: creating.route
? 'menu.explorer_menu.new_route_file'
: 'menu.explorer_menu.new_file'
)} )}
oncommit={create} oncommit={create}
oncancel={() => (creating.parent = undefined)} oncancel={() => (creating.parent = undefined)}
@@ -15,11 +15,13 @@ export const renaming: { path: string | undefined } = $state({ path: undefined }
/** /**
* The file or folder that is being made in a folder (`parent`) of the explorer: it is only made once * The file or folder that is being made in a folder (`parent`) of the explorer: it is only made once
* the user gave it a name. * the user gave it a name. With `route`, the file starts with an empty route.
*/ */
export const creating: { parent: string | undefined; kind: 'file' | 'folder' } = $state({ export const creating: { parent: string | undefined; kind: 'file' | 'folder'; route: boolean } =
$state({
parent: undefined, parent: undefined,
kind: 'file', kind: 'file',
route: false,
}); });
/** What is dragged in the explorer, and the folder it would be dropped in. */ /** What is dragged in the explorer, and the folder it would be dropped in. */
+3 -2
View File
@@ -54,7 +54,8 @@ export async function createTauriBackend(): Promise<Backend> {
cut: () => invoke('cut'), cut: () => invoke('cut'),
paste: () => invoke('paste'), paste: () => invoke('paste'),
new_file: (name, lng, lat, ele) => invoke('new_file', { name, lng, lat, ele }), new_file: (name, lng, lat, ele, route) =>
invoke('new_file', { name, lng, lat, ele, route }),
load_files: (files) => { load_files: (files) => {
const data = new Uint8Array(files.reduce((sum, file) => sum + file.data.length, 0)); const data = new Uint8Array(files.reduce((sum, file) => sum + file.data.length, 0));
let offset = 0; let offset = 0;
@@ -225,7 +226,7 @@ export async function createTauriBackend(): Promise<Backend> {
invoke<boolean>('export_files', { fileIds, options }), invoke<boolean>('export_files', { fileIds, options }),
listFolder: (path) => invoke<FolderEntry[]>('list_folder', { path }), listFolder: (path) => invoke<FolderEntry[]>('list_folder', { path }),
openUrl: (url) => invoke('open_url', { url }), openUrl: (url) => invoke('open_url', { url }),
newFile: (name, folder) => invoke<Outcome>('new_file', { name, folder }), newFile: (name, folder, route) => invoke<Outcome>('new_file', { name, folder, route }),
createFolder: (parent, name) => invoke<string>('create_folder', { parent, name }), createFolder: (parent, name) => invoke<string>('create_folder', { parent, name }),
renamePath: (path, name) => invoke<Outcome>('rename_path', { path, name }), renamePath: (path, name) => invoke<Outcome>('rename_path', { path, name }),
movePaths: (paths, folder) => invoke('move_paths', { paths, folder }), movePaths: (paths, folder) => invoke('move_paths', { paths, folder }),
+12 -3
View File
@@ -100,8 +100,11 @@ export interface DesktopFiles {
listFolder(path?: string): Promise<FolderEntry[]>; listFolder(path?: string): Promise<FolderEntry[]>;
/** Opens a web page (or an email to write) in the app of the system for it. */ /** Opens a web page (or an email to write) in the app of the system for it. */
openUrl(url: string): Promise<void>; openUrl(url: string): Promise<void>;
/** Makes a new file, written at once in `folder` (a folder of the open folder), and selects it. */ /**
newFile(name: string, folder: string): Promise<Outcome>; * Makes a new file, written at once in `folder` (a folder of the open folder), and selects it.
* With `route`, it starts with an empty route.
*/
newFile(name: string, folder: string, route: boolean): Promise<Outcome>;
/** Makes a folder in a folder of the open folder (numbered if the name is taken): its path. */ /** Makes a folder in a folder of the open folder (numbered if the name is taken): its path. */
createFolder(parent: string, name: string): Promise<string>; createFolder(parent: string, name: string): Promise<string>;
/** /**
@@ -165,7 +168,13 @@ export interface Backend {
paste(): Promise<Outcome>; paste(): Promise<Outcome>;
// Files // Files
new_file(name: string, lng?: number, lat?: number, ele?: number): Promise<Outcome>; new_file(
name: string,
lng: number | undefined,
lat: number | undefined,
ele: number | undefined,
route: boolean
): Promise<Outcome>;
load_files(files: { data: Uint8Array; name: string }[]): Promise<Outcome>; load_files(files: { data: Uint8Array; name: string }[]): Promise<Outcome>;
duplicate(): Promise<Outcome>; duplicate(): Promise<Outcome>;
delete(wholeFiles: boolean): Promise<Outcome>; delete(wholeFiles: boolean): Promise<Outcome>;
+9
View File
@@ -204,6 +204,15 @@ describe('selection', () => {
assert.equal(fileStates()[0].structure.routes.length, 3); assert.equal(fileStates()[0].structure.routes.length, 3);
}); });
it('creates a file that starts with an empty route', async () => {
assert.equal(await engine.newFile('routes', undefined, true), true);
const [state] = fileStates();
assert.equal(state.structure.routes.length, 1);
assert.equal(state.structure.routes[0].segment.length, 0);
assert.equal(state.structure.tracks.length, 0);
assert.deepEqual(get(engine.selection), { type: 'file', fileIds: [state.structure.id] });
});
it('ignores what does not exist', async () => { it('ignores what does not exist', async () => {
await load('simple'); await load('simple');
const before = get(engine.selection); const before = get(engine.selection);
+14 -7
View File
@@ -251,11 +251,18 @@ class Engine {
// Edits of the files // Edits of the files
/** /**
* Creates a file, which gets selected. With a `trackpoint`, the file starts with a track and a * Creates a file, which gets selected. With `route`, the file starts with a route. With a
* segment that hold it, in the same undo step. * `trackpoint`, the file starts with a segment that holds it, the one of its route with `route`
* or of a track otherwise, in the same undo step.
*/ */
newFile(name: string, trackpoint?: { lng: number; lat: number; ele?: number }) { newFile(
return this.run((b) => b.new_file(name, trackpoint?.lng, trackpoint?.lat, trackpoint?.ele)); name: string,
trackpoint?: { lng: number; lat: number; ele?: number },
route: boolean = false
) {
return this.run((b) =>
b.new_file(name, trackpoint?.lng, trackpoint?.lat, trackpoint?.ele, route)
);
} }
/** /**
@@ -606,10 +613,10 @@ class Engine {
/** /**
* Makes a new file in a folder of the open folder, which is written there at once and selected. * Makes a new file in a folder of the open folder, which is written there at once and selected.
* Resolves to its id. * With `route`, it starts with an empty route. Resolves to its id.
*/ */
async newFileIn(name: string, folder: string): Promise<string | undefined> { async newFileIn(name: string, folder: string, route = false): Promise<string | undefined> {
const [id] = await this.addedBy((b) => b.desktop!.newFile(name, folder), true); const [id] = await this.addedBy((b) => b.desktop!.newFile(name, folder, route), true);
return id; return id;
} }
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "Стварыць новы файл", "new_file": "Стварыць новы файл",
"new_track": "Стварыць маршрут", "new_track": "Стварыць маршрут",
"new_segment": "Стварыць сегмент", "new_segment": "Стварыць сегмент",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visualitza, edita i crea fitxers GPX en línia amb capacitats avançades de planificació de rutes i eines de processament de fitxers, mapes bonics i visualitzacions de dades detallades." "description": "Visualitza, edita i crea fitxers GPX en línia amb capacitats avançades de planificació de rutes i eines de processament de fitxers, mapes bonics i visualitzacions de dades detallades."
}, },
"menu": { "menu": {
"new": "Nou",
"new_file": "Nou fitxer", "new_file": "Nou fitxer",
"new_track": "Nou trac", "new_track": "Nou trac",
"new_segment": "Nou segment", "new_segment": "Nou segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Zobrazujte, upravujte a vytvářejte GPX soubory online s pokročilými možnostmi plánování tras a nástroji ke zpracování souborů, krásnými mapami a podrobnými vizualizacemi dat." "description": "Zobrazujte, upravujte a vytvářejte GPX soubory online s pokročilými možnostmi plánování tras a nástroji ke zpracování souborů, krásnými mapami a podrobnými vizualizacemi dat."
}, },
"menu": { "menu": {
"new": "Nový",
"new_file": "Nový soubor", "new_file": "Nový soubor",
"new_track": "Nová trasa", "new_track": "Nová trasa",
"new_segment": "Nový úsek", "new_segment": "Nový úsek",
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "New file", "new_file": "New file",
"new_track": "New track", "new_track": "New track",
"new_segment": "New segment", "new_segment": "New segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "GPX-Dateien online anzeigen, bearbeiten und erstellen mit erweiterten Routenplanungs- und Dateiverarbeitungswerkzeugen, schönen Karten und detaillierten Datenvisualisierungen." "description": "GPX-Dateien online anzeigen, bearbeiten und erstellen mit erweiterten Routenplanungs- und Dateiverarbeitungswerkzeugen, schönen Karten und detaillierten Datenvisualisierungen."
}, },
"menu": { "menu": {
"new": "Neu",
"new_file": "Neue Datei", "new_file": "Neue Datei",
"new_track": "Neue Route", "new_track": "Neue Route",
"new_segment": "Neues Segment", "new_segment": "Neues Segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "New file", "new_file": "New file",
"new_track": "New track", "new_track": "New track",
"new_segment": "New segment", "new_segment": "New segment",
+2 -1
View File
@@ -8,8 +8,8 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "New file", "new_file": "New file",
"new_route_file": "New route file",
"new_track": "New track", "new_track": "New track",
"new_route": "New route", "new_route": "New route",
"convert_to_route": "Convert to route", "convert_to_route": "Convert to route",
@@ -21,6 +21,7 @@
"explorer_menu": { "explorer_menu": {
"open": "Open", "open": "Open",
"new_file": "New file...", "new_file": "New file...",
"new_route_file": "New route file...",
"new_folder": "New folder...", "new_folder": "New folder...",
"rename": "Rename...", "rename": "Rename...",
"reveal_finder": "Reveal in Finder", "reveal_finder": "Reveal in Finder",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visualiza, edita y crea archivos GPX online con planificación avanzada de rutas y herramientas de procesamiento de archivos, bonitos mapas y visualizaciones detalladas de datos." "description": "Visualiza, edita y crea archivos GPX online con planificación avanzada de rutas y herramientas de procesamiento de archivos, bonitos mapas y visualizaciones detalladas de datos."
}, },
"menu": { "menu": {
"new": "Nuevo",
"new_file": "Nuevo archivo", "new_file": "Nuevo archivo",
"new_track": "Nuevo recorrido", "new_track": "Nuevo recorrido",
"new_segment": "Nuevo segmento", "new_segment": "Nuevo segmento",
-1
View File
@@ -8,7 +8,6 @@
"description": "Ikusi, editatu eta sortu GPX fitxategiak bideak bilatzeko aukera aurreratuarekin eta fitxategiak prozesatzeko tresnekin, mapa ederrekin eta datuak ikusteko xehetasun handiarekin." "description": "Ikusi, editatu eta sortu GPX fitxategiak bideak bilatzeko aukera aurreratuarekin eta fitxategiak prozesatzeko tresnekin, mapa ederrekin eta datuak ikusteko xehetasun handiarekin."
}, },
"menu": { "menu": {
"new": "Berria",
"new_file": "Fitxategi berria", "new_file": "Fitxategi berria",
"new_track": "Arrasto berria", "new_track": "Arrasto berria",
"new_segment": "Segmentu berria", "new_segment": "Segmentu berria",
-1
View File
@@ -8,7 +8,6 @@
"description": "Katsele, muokkaa ja luo GPX-tiedostoja verkossa edistyneitä reittisuunnittelutyökaluja, näyttäviä karttoja ja yksityiskohtaisia esityksiä hyödyntäen." "description": "Katsele, muokkaa ja luo GPX-tiedostoja verkossa edistyneitä reittisuunnittelutyökaluja, näyttäviä karttoja ja yksityiskohtaisia esityksiä hyödyntäen."
}, },
"menu": { "menu": {
"new": "Uusi",
"new_file": "Uusi tiedosto", "new_file": "Uusi tiedosto",
"new_track": "Uusi reitti", "new_track": "Uusi reitti",
"new_segment": "Uusi osio", "new_segment": "Uusi osio",
-1
View File
@@ -8,7 +8,6 @@
"description": "Affichez, éditez et créez des fichiers GPX en ligne avec des outils avancés de planification et de traitement de fichiers, de magnifiques cartes et des visualisations de données détaillées." "description": "Affichez, éditez et créez des fichiers GPX en ligne avec des outils avancés de planification et de traitement de fichiers, de magnifiques cartes et des visualisations de données détaillées."
}, },
"menu": { "menu": {
"new": "Nouveau",
"new_file": "Nouveau fichier", "new_file": "Nouveau fichier",
"new_track": "Nouvelle piste", "new_track": "Nouvelle piste",
"new_segment": "Nouveau segment", "new_segment": "Nouveau segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "חדש",
"new_file": "קובץ חדש", "new_file": "קובץ חדש",
"new_track": "New track", "new_track": "New track",
"new_segment": "קטע חדש", "new_segment": "קטע חדש",
-1
View File
@@ -8,7 +8,6 @@
"description": "GPX fájlok online szerkesztése és létrehozása fejlett útvonal tervezési képességekkel és fájlfeldolgozó eszközökkel, gyönyörű térképekkel és részletekkel." "description": "GPX fájlok online szerkesztése és létrehozása fejlett útvonal tervezési képességekkel és fájlfeldolgozó eszközökkel, gyönyörű térképekkel és részletekkel."
}, },
"menu": { "menu": {
"new": "Új.",
"new_file": "Új fájl ", "new_file": "Új fájl ",
"new_track": "Új útvonal", "new_track": "Új útvonal",
"new_segment": "Új szakasz", "new_segment": "Új szakasz",
-1
View File
@@ -8,7 +8,6 @@
"description": "Lihat, edit, dan buat berkas GPX secara online dengan fitur perencanaan rute canggih dan alat pemrosesan berkas, peta yang indah, dan visualisasi data yang detail." "description": "Lihat, edit, dan buat berkas GPX secara online dengan fitur perencanaan rute canggih dan alat pemrosesan berkas, peta yang indah, dan visualisasi data yang detail."
}, },
"menu": { "menu": {
"new": "Baru",
"new_file": "Berkas baru", "new_file": "Berkas baru",
"new_track": "Trek baru", "new_track": "Trek baru",
"new_segment": "Segmen Baru", "new_segment": "Segmen Baru",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visualizza, modifica e genera file GPX online con avanzate funzionalità di pianificazione del percorso e strumenti di elaborazione dei file, mappe eleganti e visualizzazione dettagliata dei dati." "description": "Visualizza, modifica e genera file GPX online con avanzate funzionalità di pianificazione del percorso e strumenti di elaborazione dei file, mappe eleganti e visualizzazione dettagliata dei dati."
}, },
"menu": { "menu": {
"new": "Nuova",
"new_file": "Nuovo file", "new_file": "Nuovo file",
"new_track": "Nuova traccia", "new_track": "Nuova traccia",
"new_segment": "Nuovo segmento", "new_segment": "Nuovo segmento",
-1
View File
@@ -8,7 +8,6 @@
"description": "고급 경로 계획과 파일 처리 도구를 이용하여 온라인에서 GPX 파일을 확인하고, 편집하고, 생성하세요. 아름다운 지도와 정밀한 데이터 시각화도 지원합니다." "description": "고급 경로 계획과 파일 처리 도구를 이용하여 온라인에서 GPX 파일을 확인하고, 편집하고, 생성하세요. 아름다운 지도와 정밀한 데이터 시각화도 지원합니다."
}, },
"menu": { "menu": {
"new": "새 파일",
"new_file": "새 파일", "new_file": "새 파일",
"new_track": "새 트랙", "new_track": "새 트랙",
"new_segment": "새 세그먼트", "new_segment": "새 세그먼트",
-1
View File
@@ -8,7 +8,6 @@
"description": "Peržiūrėkite, redaguokite ir kurkite GPX failus internete naudodami išplėstines maršruto planavimo galimybes ir failų apdorojimo įrankius, gražius žemėlapius ir išsamias duomenų vizualizacijas." "description": "Peržiūrėkite, redaguokite ir kurkite GPX failus internete naudodami išplėstines maršruto planavimo galimybes ir failų apdorojimo įrankius, gražius žemėlapius ir išsamias duomenų vizualizacijas."
}, },
"menu": { "menu": {
"new": "Naujas",
"new_file": "Naujas failas", "new_file": "Naujas failas",
"new_track": "Nauja trasa", "new_track": "Nauja trasa",
"new_segment": "Naujas segmentas", "new_segment": "Naujas segmentas",
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "New file", "new_file": "New file",
"new_track": "New track", "new_track": "New track",
"new_segment": "New segment", "new_segment": "New segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Bekijk, bewerk en maak GPX-bestanden met geavanceerde mogelijkheden voor route planning en nabewerkingsmogelijkheden, prachtige kaarten en gedetailleerde visualisaties." "description": "Bekijk, bewerk en maak GPX-bestanden met geavanceerde mogelijkheden voor route planning en nabewerkingsmogelijkheden, prachtige kaarten en gedetailleerde visualisaties."
}, },
"menu": { "menu": {
"new": "Nieuw",
"new_file": "Nieuw bestand", "new_file": "Nieuw bestand",
"new_track": "Nieuwe route", "new_track": "Nieuwe route",
"new_segment": "Nieuw segment", "new_segment": "Nieuw segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Vis, rediger og opprett GPX-filer online med avansert ruteplanlegging og filbehandling, vakre kart og detaljert visualisering." "description": "Vis, rediger og opprett GPX-filer online med avansert ruteplanlegging og filbehandling, vakre kart og detaljert visualisering."
}, },
"menu": { "menu": {
"new": "Ny",
"new_file": "Ny fil", "new_file": "Ny fil",
"new_track": "Nytt spor", "new_track": "Nytt spor",
"new_segment": "Nytt segment", "new_segment": "Nytt segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Przeglądaj, edytuj i twórz pliki GPX online z zaawansowanymi możliwościami planowania trasy i narzędziami do przetwarzania plików, pięknymi mapami i szczegółowymi wizualizacjami danych." "description": "Przeglądaj, edytuj i twórz pliki GPX online z zaawansowanymi możliwościami planowania trasy i narzędziami do przetwarzania plików, pięknymi mapami i szczegółowymi wizualizacjami danych."
}, },
"menu": { "menu": {
"new": "Nowy",
"new_file": "Nowy plik", "new_file": "Nowy plik",
"new_track": "Nowa ścieżka", "new_track": "Nowa ścieżka",
"new_segment": "Nowy segment", "new_segment": "Nowy segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visualizar, editar e criar arquivos GPX online com capacidade de roteamento avançado e ferramentas de processamento de arquivos, belos mapas e visualização de dados detalhados." "description": "Visualizar, editar e criar arquivos GPX online com capacidade de roteamento avançado e ferramentas de processamento de arquivos, belos mapas e visualização de dados detalhados."
}, },
"menu": { "menu": {
"new": "Novo",
"new_file": "Novo arquivo", "new_file": "Novo arquivo",
"new_track": "Novo percurso", "new_track": "Novo percurso",
"new_segment": "Novo segmento", "new_segment": "Novo segmento",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visualizar, editar, e criar arquivos GPX \"online\" com capacidades avançadas de planeamento de rotas" "description": "Visualizar, editar, e criar arquivos GPX \"online\" com capacidades avançadas de planeamento de rotas"
}, },
"menu": { "menu": {
"new": "Novo",
"new_file": "Novo arquivo", "new_file": "Novo arquivo",
"new_track": "Novo percurso", "new_track": "Novo percurso",
"new_segment": "Novo segmento", "new_segment": "Novo segmento",
-1
View File
@@ -8,7 +8,6 @@
"description": "Vizualizați, editați și creați fișiere GPX online cu capacități avansate de planificare a rutelor și instrumente de procesare a fișierelor, hărți frumoase și vizualizări de date detaliate." "description": "Vizualizați, editați și creați fișiere GPX online cu capacități avansate de planificare a rutelor și instrumente de procesare a fișierelor, hărți frumoase și vizualizări de date detaliate."
}, },
"menu": { "menu": {
"new": "Nou",
"new_file": "Fișier nou", "new_file": "Fișier nou",
"new_track": "Traseu nou", "new_track": "Traseu nou",
"new_segment": "Segment nou", "new_segment": "Segment nou",
-1
View File
@@ -8,7 +8,6 @@
"description": "Просматривайте, редактируйте, создавайте GPX файлы онлайн с расширенными возможностями планирования маршрутов и инструментами обработки файлов, красивыми картами и детальной визуализацией данных." "description": "Просматривайте, редактируйте, создавайте GPX файлы онлайн с расширенными возможностями планирования маршрутов и инструментами обработки файлов, красивыми картами и детальной визуализацией данных."
}, },
"menu": { "menu": {
"new": "Новый",
"new_file": "Новый файл", "new_file": "Новый файл",
"new_track": "Новый трек", "new_track": "Новый трек",
"new_segment": "Новый сегмент", "new_segment": "Новый сегмент",
-1
View File
@@ -8,7 +8,6 @@
"description": "Pregledajte, uređujte i kreirajte GPX datoteke uz napredne mogućnosti planiranja ruta i alatima za obradu datoteka, prelepim mapama i detaljnim vizualizacijama podataka." "description": "Pregledajte, uređujte i kreirajte GPX datoteke uz napredne mogućnosti planiranja ruta i alatima za obradu datoteka, prelepim mapama i detaljnim vizualizacijama podataka."
}, },
"menu": { "menu": {
"new": "Novo",
"new_file": "Nova datoteka", "new_file": "Nova datoteka",
"new_track": "Nova ruta", "new_track": "Nova ruta",
"new_segment": "Novi segment", "new_segment": "Novi segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Visa, redigera och skapa GPX-filer online med avancerade ruttplaneringsfunktioner, verktyg för filhantering, tilltalande kartor och detaljerade datavisualiseringar." "description": "Visa, redigera och skapa GPX-filer online med avancerade ruttplaneringsfunktioner, verktyg för filhantering, tilltalande kartor och detaljerade datavisualiseringar."
}, },
"menu": { "menu": {
"new": "Nytt",
"new_file": "Ny fil", "new_file": "Ny fil",
"new_track": "Nytt spår", "new_track": "Nytt spår",
"new_segment": "Nytt segment", "new_segment": "Nytt segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations." "description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
}, },
"menu": { "menu": {
"new": "New",
"new_file": "New file", "new_file": "New file",
"new_track": "New track", "new_track": "New track",
"new_segment": "New segment", "new_segment": "New segment",
-1
View File
@@ -8,7 +8,6 @@
"description": "Gelişmiş rota planlama özellikleri ve dosya işleme araçları, harika haritalar ve ayrıntılı veri görselleştirmeleri ile GPX dosyalarını çevrimiçi görüntüleyin, düzenleyin ve oluşturun." "description": "Gelişmiş rota planlama özellikleri ve dosya işleme araçları, harika haritalar ve ayrıntılı veri görselleştirmeleri ile GPX dosyalarını çevrimiçi görüntüleyin, düzenleyin ve oluşturun."
}, },
"menu": { "menu": {
"new": "Yeni",
"new_file": "Yeni dosya", "new_file": "Yeni dosya",
"new_track": "Yeni yol", "new_track": "Yeni yol",
"new_segment": "Yeni kısım", "new_segment": "Yeni kısım",
-1
View File
@@ -8,7 +8,6 @@
"description": "Переглядайте, редагуйте та створюйте GPX-файли онлайн за допомогою розширених можливостей планування маршрутів та інструментів обробки файлів, красивих карт і детальної візуалізації даних." "description": "Переглядайте, редагуйте та створюйте GPX-файли онлайн за допомогою розширених можливостей планування маршрутів та інструментів обробки файлів, красивих карт і детальної візуалізації даних."
}, },
"menu": { "menu": {
"new": "Новий",
"new_file": "Новий файл", "new_file": "Новий файл",
"new_track": "Новий трек", "new_track": "Новий трек",
"new_segment": "Новий сегмент", "new_segment": "Новий сегмент",
-1
View File
@@ -8,7 +8,6 @@
"description": "Xem, chỉnh sửa và tạo tệp tin GPX trực tuyến với các tính năng vẽ bản đồ nâng cao cùng với nhiều công cụ trợ giúp, bản đồ đa dạng và dữ liệu trực quan." "description": "Xem, chỉnh sửa và tạo tệp tin GPX trực tuyến với các tính năng vẽ bản đồ nâng cao cùng với nhiều công cụ trợ giúp, bản đồ đa dạng và dữ liệu trực quan."
}, },
"menu": { "menu": {
"new": "Mới",
"new_file": "Tạo tệp tin mới", "new_file": "Tạo tệp tin mới",
"new_track": "Tạo tuyến đường mới", "new_track": "Tạo tuyến đường mới",
"new_segment": "Tạo đoạn đường mới", "new_segment": "Tạo đoạn đường mới",
-1
View File
@@ -8,7 +8,6 @@
"description": "線上查看、編輯和建立 GPX 檔案,包含高級路線規劃、檔案管理器、精美的地圖和詳細的資料可視化功能。" "description": "線上查看、編輯和建立 GPX 檔案,包含高級路線規劃、檔案管理器、精美的地圖和詳細的資料可視化功能。"
}, },
"menu": { "menu": {
"new": "新建",
"new_file": "新增檔案", "new_file": "新增檔案",
"new_track": "新增路徑", "new_track": "新增路徑",
"new_segment": "新增路段", "new_segment": "新增路段",
-1
View File
@@ -8,7 +8,6 @@
"description": "在线查看、编辑和创建 GPX 文件,包含高级路线规划、文件管理器、精美的地图和详细的数据可视化功能。" "description": "在线查看、编辑和创建 GPX 文件,包含高级路线规划、文件管理器、精美的地图和详细的数据可视化功能。"
}, },
"menu": { "menu": {
"new": "新建",
"new_file": "新建文件", "new_file": "新建文件",
"new_track": "新建轨迹", "new_track": "新建轨迹",
"new_segment": "新建路段", "new_segment": "新建路段",