Rebrand: Renamr -> Aurora Curator (new logo/app icon, renamed crate aurora-curator-core, package/identifier/window-title updates, README + in-app text)
@@ -100,6 +100,34 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "aurora-curator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aurora-curator-core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
"tokio",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aurora-curator-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"mockito",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"rhai",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
@@ -2770,34 +2798,6 @@ version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "renamr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"renamr-core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
"tokio",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renamr-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"mockito",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"rhai",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/renamr-core", "src-tauri"]
|
||||
members = ["crates/aurora-curator-core", "src-tauri"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Renamr
|
||||
# Aurora Curator
|
||||
|
||||
A fast, cross-platform media renamer — a ground-up rewrite of FileBot (via its FB-Mod
|
||||
fork), built from scratch as its own project: new stack, new architecture, new branding.
|
||||
@@ -48,7 +48,7 @@ The original is a ~20k+ line Java/Swing app built with Ant. The goals were: fast
|
||||
startup and UI, faster matching/renaming, a codebase that's actually pleasant to work
|
||||
in, and a genuine rebrand — not a coat of paint.
|
||||
|
||||
Renamr is **Tauri**: a Rust core (`crates/renamr-core`, `src-tauri`) plus a
|
||||
Aurora Curator is **Tauri**: a Rust core (`crates/aurora-curator-core`, `src-tauri`) plus a
|
||||
TypeScript/React frontend (`src`), compiled to a small native binary per platform
|
||||
(Windows/macOS/Linux) with a system webview instead of shipping a bundled browser
|
||||
engine. That's the concrete "faster" win over both the original JVM/Swing app and
|
||||
@@ -69,7 +69,7 @@ template syntax compatible with FileBot conventions, so existing automation most
|
||||
keeps working.
|
||||
|
||||
One deliberate gap: FileBot's `{ }` expressions ran arbitrary **Groovy** code
|
||||
(JVM-only). Renamr's Rust core has no Groovy engine, so expressions are evaluated with
|
||||
(JVM-only). Aurora Curator's Rust core has no Groovy engine, so expressions are evaluated with
|
||||
[Rhai](https://rhai.rs/) instead. Simple bindings (`{n}`, `{s00e00}`, `{t}`) are
|
||||
identical. More complex chained-method expressions will need small syntax tweaks
|
||||
(e.g. Groovy's `.toUpperCase()` → Rhai's `.to_upper()`) — Rhai already ships most of the
|
||||
@@ -79,7 +79,7 @@ translate directly.
|
||||
### Naming scheme syntax
|
||||
|
||||
Every `{ }` token in an expression is resolved in this order
|
||||
(`crates/renamr-core/src/format/expression.rs::compile_scriptlet`):
|
||||
(`crates/aurora-curator-core/src/format/expression.rs::compile_scriptlet`):
|
||||
|
||||
1. **A single bare identifier** (`{n}`, `{season}`) is a direct binding lookup — the
|
||||
fast path, and how FileBot's own short field names keep working unmodified.
|
||||
@@ -109,12 +109,12 @@ a subfolder" rather than rejecting it.
|
||||
A naming scheme that includes `/` (e.g. `{show name}/Season {season number}/{episode
|
||||
number} - {episode name}`) moves the file into that subfolder as part of renaming,
|
||||
creating any missing intermediate folders automatically
|
||||
(`crates/renamr-core/src/rename/mod.rs::rename_one` → `fs::create_dir_all`). This is
|
||||
(`crates/aurora-curator-core/src/rename/mod.rs::rename_one` → `fs::create_dir_all`). This is
|
||||
still always scoped to the file's own directory tree — the target is resolved relative
|
||||
to the source file's parent directory, never as an absolute path, and every path
|
||||
component is validated (`validate_relative_target`) to reject empty segments (a
|
||||
leading, trailing, or doubled `/` — which also catches an absolute-looking
|
||||
`/etc/passwd`-style path), `.`/`..` segments, and backslashes (Renamr always uses `/`
|
||||
`/etc/passwd`-style path), `.`/`..` segments, and backslashes (Aurora Curator always uses `/`
|
||||
for the naming scheme's folder separator regardless of OS). If the resolved target
|
||||
already exists and isn't the same underlying file, what happens next is up to the
|
||||
chosen conflict policy — Skip (default), Auto-number, or Override; see item 6 of the
|
||||
@@ -123,7 +123,7 @@ porting roadmap below for the full behavior of each.
|
||||
## Project layout
|
||||
|
||||
```
|
||||
crates/renamr-core/ Pure Rust core: naming-expression engine (ported), local
|
||||
crates/aurora-curator-core/ Pure Rust core: naming-expression engine (ported), local
|
||||
filename-based season/episode *and movie title/year* matching
|
||||
(ported, `matching/`), plus filename-based release-tag parsing in
|
||||
the same module (`guess_release_info` — resolution, source, video/
|
||||
@@ -141,7 +141,7 @@ crates/renamr-core/ Pure Rust core: naming-expression engine (ported), local
|
||||
action produced each batch). Not yet ported: full media metadata
|
||||
bindings, other web datasource clients (TVDB/AniDB/etc).
|
||||
No Tauri dependency, so it builds and unit-tests standalone.
|
||||
src-tauri/ Tauri app shell: exposes renamr-core as commands to the frontend
|
||||
src-tauri/ Tauri app shell: exposes aurora-curator-core as commands to the frontend
|
||||
(naming-scheme rendering, filename matching for both TV and
|
||||
movies, TMDB metadata fetch — `metadata.rs` for TV episodes,
|
||||
plus a movie-fetch command alongside it — executing a rename
|
||||
@@ -172,7 +172,7 @@ toolchain; macOS/Windows need Xcode / the VS Build Tools + WebView2 respectively
|
||||
|
||||
```sh
|
||||
# Rust core (no UI deps needed):
|
||||
cargo test -p renamr-core
|
||||
cargo test -p aurora-curator-core
|
||||
|
||||
# Full app in dev mode (opens a native window):
|
||||
npm install
|
||||
@@ -182,10 +182,10 @@ npm run tauri dev
|
||||
npm run tauri build
|
||||
```
|
||||
|
||||
Verified in the environment this was built in: `cargo test -p renamr-core` (all
|
||||
Verified in the environment this was built in: `cargo test -p aurora-curator-core` (all
|
||||
passing, including the TMDB client tests — those run against a local mock HTTP server
|
||||
via `mockito`, no live network or API key needed to build/test), `cargo check -p
|
||||
renamr` (compiles clean against real Tauri + GTK/WebKit system libraries), and `npm run
|
||||
aurora-curator` (compiles clean against real Tauri + GTK/WebKit system libraries), and `npm run
|
||||
build` (TypeScript type-checks, Vite bundles). The actual GUI window has **not** been
|
||||
visually verified anywhere yet — that sandbox has no display. Run `npm run tauri dev`
|
||||
on your machine to see and click through it for the first time.
|
||||
@@ -196,7 +196,7 @@ Everything else in the app works fully offline; "Fetch Data" is the one feature
|
||||
needs a (free) [TMDB](https://www.themoviedb.org/) account:
|
||||
|
||||
1. Create a TMDB account, then go to Settings → API in your account and generate an
|
||||
**API Read Access Token** (not the older numeric "API Key" — Renamr uses the Bearer
|
||||
**API Read Access Token** (not the older numeric "API Key" — Aurora Curator uses the Bearer
|
||||
token method TMDB documents as the default).
|
||||
2. In the app, click the key icon ("TMDB Settings") next to Fetch Data and paste the
|
||||
token in. It's saved locally in the OS app-config directory and only ever sent to
|
||||
@@ -210,38 +210,38 @@ ported from, for anyone picking this up:
|
||||
1. **Full naming-scheme bindings** — `net.filebot.format.MediaBindingBean` (1354 lines):
|
||||
all the `{n}`, `{s00e00}`, `{airdate}`, `{vf}` (video format), etc. binding
|
||||
properties. A filename-tag-based subset is now ported and wired to "Match"
|
||||
(`guess_release_info` in `crates/renamr-core/src/matching`): `{vf}`/`{resolution}`,
|
||||
(`guess_release_info` in `crates/aurora-curator-core/src/matching`): `{vf}`/`{resolution}`,
|
||||
`{source}`, `{vc}`/`{video codec}`, `{ac}`/`{audio codec}`, `{group}`/`{release
|
||||
group}`. What's still missing is everything MediaBindingBean derives from actually
|
||||
*probing* the media file (real video/audio stream resolution, duration, bitrate,
|
||||
frame rate, file size/hash, etc.) — Renamr ships no ffprobe/MediaInfo dependency, so
|
||||
frame rate, file size/hash, etc.) — Aurora Curator ships no ffprobe/MediaInfo dependency, so
|
||||
those bindings only work when a release name happens to carry the info as text.
|
||||
2. **String/expression helper methods** — `net.filebot.format.ExpressionFormatMethods`
|
||||
(636 lines): FileBot-specific Groovy string extensions like `.pad()`, `.sortName()`.
|
||||
Only a small subset (`any`, `all_of`, `concat`) is ported so far
|
||||
(`crates/renamr-core/src/format/functions.rs`); Rhai's built-ins cover some of the
|
||||
(`crates/aurora-curator-core/src/format/functions.rs`); Rhai's built-ins cover some of the
|
||||
rest already.
|
||||
3. **Matching/similarity engine** — `net.filebot.similarity.*`. Local filename-based
|
||||
season/episode detection is done (`crates/renamr-core/src/matching`, ported from the
|
||||
season/episode detection is done (`crates/aurora-curator-core/src/matching`, ported from the
|
||||
pattern-matching half of `SeasonEpisodeMatcher.java`, adapted to Rust's `regex` crate
|
||||
which lacks lookahead/lookbehind — wired to the "Match" button), and so is a local
|
||||
movie title/year heuristic (`guess_movie` in the same module) used as a fallback
|
||||
whenever a filename carries no season/episode marker — a release name is either a TV
|
||||
episode or a movie, never both. Title disambiguation is also done
|
||||
(`crates/renamr-core/src/similarity`, a partial port of the *scoring* idea behind
|
||||
(`crates/aurora-curator-core/src/similarity`, a partial port of the *scoring* idea behind
|
||||
`EpisodeMetrics.java`'s fuzzy title-matching cascade — Sorensen-Dice similarity over
|
||||
normalized-title character bigrams), shared by both the TV and movie TMDB lookups:
|
||||
"Fetch Data" picks whichever TMDB search result is textually closest to the local
|
||||
guess rather than just trusting TMDB's relevance/popularity-ranked first hit, and
|
||||
reports "not found" instead of guessing when nothing in the results is a plausible
|
||||
match at all. `DateMatcher` (numbering/air-date-based identification, the other half
|
||||
of the original `EpisodeMetrics`) is not ported — Renamr doesn't do date-based
|
||||
of the original `EpisodeMetrics`) is not ported — Aurora Curator doesn't do date-based
|
||||
episode identification.
|
||||
4. **Media detection** — `net.filebot.media.MediaDetection` (1356 lines): ties file-name
|
||||
parsing + matching together to auto-identify what a file is.
|
||||
5. **Web datasource clients** — `net.filebot.web.*` (~40 files): TheMovieDB, TheTVDB,
|
||||
AniDB, OMDb, TVmaze, OpenSubtitles, etc. TMDB is done for both TV and movies
|
||||
(`crates/renamr-core/src/web/tmdb.rs`, `reqwest` + `serde_json`, Bearer-token auth) —
|
||||
(`crates/aurora-curator-core/src/web/tmdb.rs`, `reqwest` + `serde_json`, Bearer-token auth) —
|
||||
search-by-name plus per-season episode lookup for TV, search-by-title (optionally
|
||||
narrowed by release year via TMDB's own `primary_release_year` filter) for movies —
|
||||
both wired to "Fetch Data", disambiguating multi-result searches via the
|
||||
@@ -251,7 +251,7 @@ ported from, for anyone picking this up:
|
||||
6. **Rename engine + CLI** — `net.filebot.cli.CmdlineOperations` (1235 lines) and
|
||||
`ArgumentBean` (415 lines, the full flag surface: `-rename`, `--db`, `--format`,
|
||||
`--action`, `--conflict`, `-get-subtitles`, `-script`, etc.). The rename operation is
|
||||
done (`crates/renamr-core/src/rename`, wired to the UI's Rename button behind a
|
||||
done (`crates/aurora-curator-core/src/rename`, wired to the UI's Rename button behind a
|
||||
confirmation step) including the `move`-into-subfolder `--action` variant — a naming
|
||||
scheme like `{n}/{s00e00}` organizes files into subfolders, creating any missing
|
||||
folders automatically, always scoped to the file's own directory tree. The
|
||||
@@ -275,7 +275,7 @@ ported from, for anyone picking this up:
|
||||
not the original's drag-and-drop-within-a-list interaction.
|
||||
8. **Undo/history for renames** — done, as a flat JSON-backed undo stack rather than a
|
||||
full port of `net.filebot.HistoryDialog`'s session/element log
|
||||
(`crates/renamr-core/src/history`, `src-tauri/src/history.rs`). Every successful
|
||||
(`crates/aurora-curator-core/src/history`, `src-tauri/src/history.rs`). Every successful
|
||||
rename is recorded to a `history.json` in the OS app-config directory (same location
|
||||
as `settings.json`); the History panel lists past batches and can revert one back to
|
||||
its original paths, skipping (never overwriting) any file whose original path now
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "renamr-core"
|
||||
name = "aurora-curator-core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Renamr core: naming-expression engine, media matching, and rename engine (UI-agnostic)."
|
||||
description = "Aurora Curator core: naming-expression engine, media matching, and rename engine (UI-agnostic)."
|
||||
|
||||
[dependencies]
|
||||
# Embeddable scripting engine powering the {expression} naming-scheme language.
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Renamr's naming-scheme expression language.
|
||||
//! Aurora Curator's naming-scheme expression language.
|
||||
//!
|
||||
//! A format string is literal text with `{ ... }` scriptlets spliced in, e.g.
|
||||
//! `"{n} - {s00e00} - {t}"`
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Rename history: a persisted log of past rename batches (what changed, from what to
|
||||
//! what) and the ability to revert one - Renamr's only safety net beyond the
|
||||
//! what) and the ability to revert one - Aurora Curator's only safety net beyond the
|
||||
//! confirmation-before-execute step, since a plain filesystem rename has no OS-level
|
||||
//! undo. Loosely inspired by `net.filebot.HistoryDialog`/the original's rename-history
|
||||
//! log, but as a flat JSON-backed undo stack (see [`push_batch`]) rather than a full
|
||||
@@ -93,7 +93,7 @@ fn revert_one(entry: &HistoryEntry, action: RenameAction) -> RevertOutcome {
|
||||
};
|
||||
}
|
||||
|
||||
// The original's parent directory should still exist (nothing in Renamr
|
||||
// The original's parent directory should still exist (nothing in Aurora Curator
|
||||
// deletes directories), but recreate it defensively in case it was removed
|
||||
// by hand since - cheap, and `create_dir_all` is a no-op when it's already
|
||||
// there.
|
||||
@@ -175,7 +175,7 @@ mod tests {
|
||||
|
||||
fn temp_dir() -> std::path::PathBuf {
|
||||
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
let dir = std::env::temp_dir().join(format!("renamr_history_test_{}_{}", std::process::id(), n));
|
||||
let dir = std::env::temp_dir().join(format!("aurora_curator_history_test_{}_{}", std::process::id(), n));
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
dir
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Renamr core: the UI-agnostic engine behind the Renamr media renamer.
|
||||
//! Aurora Curator core: the UI-agnostic engine behind the Aurora Curator media renamer.
|
||||
//!
|
||||
//! This crate has no Tauri/UI dependency so it can be built and unit-tested standalone.
|
||||
//! It's the piece being ported first from the original FileBot (Java) codebase; see
|
||||
@@ -175,7 +175,7 @@ pub fn guess_movie(filename: &str) -> Option<MovieGuess> {
|
||||
/// (capture/rip type), video codec, audio codec, and release group. Ports the
|
||||
/// filename-parsing half of the original FileBot's `ReleaseInfo` class - the same
|
||||
/// regex-based tag recognition `MediaBindingBean` falls back to when no real media
|
||||
/// probe (ffprobe/MediaInfo) is available, which Renamr never has, so this is the
|
||||
/// probe (ffprobe/MediaInfo) is available, which Aurora Curator never has, so this is the
|
||||
/// only source for these fields here. Deliberately independent of `guess_episode`/
|
||||
/// `guess_movie`: a release name carries these tags (or doesn't) regardless of
|
||||
/// whether it's a TV episode or a movie, so callers run this alongside whichever of
|
||||
@@ -17,7 +17,7 @@
|
||||
//! the caller a confirmation of the full plan (and let them choose both a
|
||||
//! [`RenameAction`] and a [`ConflictPolicy`]) before calling [`execute_renames`]; every
|
||||
//! batch that action actually touches a file for is also recorded to the rename-history
|
||||
//! log one layer up (`renamr_core::history`), so it can be undone from there. Undoing
|
||||
//! log one layer up (`aurora_curator_core::history`), so it can be undone from there. Undoing
|
||||
//! always uses skip-on-conflict regardless of what policy the original rename used
|
||||
//! (see `history::revert_one`) - overwriting an unrelated file as a side effect of an
|
||||
//! *undo* would be a much worse surprise than leaving it for the user to sort out by
|
||||
@@ -47,7 +47,7 @@ pub struct RenamePlanItem {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RenameAction {
|
||||
/// Renames/moves the file in place - the only action Renamr had before this. The
|
||||
/// Renames/moves the file in place - the only action Aurora Curator had before this. The
|
||||
/// source file no longer exists afterward; the item now lives at the target path.
|
||||
Move,
|
||||
/// Copies the file's contents to the target path, leaving the source untouched.
|
||||
@@ -118,7 +118,7 @@ pub struct RenameResult {
|
||||
/// no empty components (a leading, trailing, or doubled `/` - which also catches an
|
||||
/// absolute-looking `/etc/passwd`-style path, since splitting it on `/` yields a
|
||||
/// leading empty component), and no `.`/`..` components. Backslashes are rejected
|
||||
/// outright rather than treated as an alternate separator - Renamr always uses `/` for
|
||||
/// outright rather than treated as an alternate separator - Aurora Curator always uses `/` for
|
||||
/// the naming scheme's folder separator regardless of OS, so a literal `\` in a
|
||||
/// computed name is almost certainly a mistake, not an intentional Windows path.
|
||||
fn validate_relative_target(name: &str) -> Result<(), &'static str> {
|
||||
@@ -322,7 +322,7 @@ mod tests {
|
||||
/// the real filesystem. Returns (dir, file_path).
|
||||
fn temp_file_setup(file_name: &str, contents: &str) -> (std::path::PathBuf, std::path::PathBuf) {
|
||||
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
let dir = std::env::temp_dir().join(format!("renamr_rename_test_{}_{}", std::process::id(), n));
|
||||
let dir = std::env::temp_dir().join(format!("aurora_curator_rename_test_{}_{}", std::process::id(), n));
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
let file = dir.join(file_name);
|
||||
fs::write(&file, contents).unwrap();
|
||||
@@ -7,7 +7,7 @@
|
||||
//! rank first - TMDB ranks by relevance/popularity, not by exact name match, so a
|
||||
//! more popular but differently-named show can otherwise beat an exact-but-less-popular
|
||||
//! one straight to the top of the list. `DateMatcher` (the other half of the original
|
||||
//! `EpisodeMetrics`) is not ported - Renamr doesn't do date-based episode identification.
|
||||
//! `EpisodeMetrics`) is not ported - Aurora Curator doesn't do date-based episode identification.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -37,7 +37,7 @@ struct SearchTvResponse {
|
||||
results: Vec<TvResult>,
|
||||
}
|
||||
|
||||
/// One TMDB search hit. Only the fields Renamr actually uses are kept.
|
||||
/// One TMDB search hit. Only the fields Aurora Curator actually uses are kept.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct TvResult {
|
||||
pub id: u64,
|
||||
@@ -75,7 +75,7 @@ struct SearchMovieResponse {
|
||||
results: Vec<MovieResult>,
|
||||
}
|
||||
|
||||
/// One TMDB movie search hit. Only the fields Renamr actually uses are kept.
|
||||
/// One TMDB movie search hit. Only the fields Aurora Curator actually uses are kept.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct MovieResult {
|
||||
pub id: u64,
|
||||
@@ -1,5 +1,5 @@
|
||||
use renamr_core::format::ExpressionFormat;
|
||||
use renamr_core::string_bindings;
|
||||
use aurora_curator_core::format::ExpressionFormat;
|
||||
use aurora_curator_core::string_bindings;
|
||||
|
||||
#[test]
|
||||
fn simple_variable_substitution() {
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Renamr</title>
|
||||
<title>Aurora Curator</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "renamr",
|
||||
"name": "aurora-curator",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "renamr",
|
||||
"name": "aurora-curator",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.1.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "renamr",
|
||||
"name": "aurora-curator",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,46 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generates Renamr's app icon set (placeholder branding, easy to swap later).
|
||||
"""Generates Aurora Curator's app icon set from the real logo mark.
|
||||
|
||||
Source master: scripts/logo/icon-source.png (the folder/aurora/filmstrip mark,
|
||||
square-padded, transparent background - cropped from the full lockup that also
|
||||
includes the "AURORA CURATOR" wordmark, which isn't used for the app icon itself).
|
||||
|
||||
Produces the sizes Tauri's bundler expects in src-tauri/icons/.
|
||||
"""
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from PIL import Image
|
||||
import os
|
||||
|
||||
OUT = os.path.join(os.path.dirname(__file__), "..", "src-tauri", "icons")
|
||||
HERE = os.path.dirname(__file__)
|
||||
SRC = os.path.join(HERE, "logo", "icon-source.png")
|
||||
OUT = os.path.join(HERE, "..", "src-tauri", "icons")
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
|
||||
SIZE = 1024
|
||||
BG_TOP = (91, 76, 245) # indigo
|
||||
BG_BOTTOM = (56, 189, 178) # teal
|
||||
FG = (255, 255, 255)
|
||||
master = Image.open(SRC).convert("RGBA")
|
||||
if master.size != (1024, 1024):
|
||||
master = master.resize((1024, 1024), Image.LANCZOS)
|
||||
|
||||
def make_master():
|
||||
img = Image.new("RGB", (SIZE, SIZE), BG_TOP)
|
||||
px = img.load()
|
||||
for y in range(SIZE):
|
||||
t = y / SIZE
|
||||
r = int(BG_TOP[0] * (1 - t) + BG_BOTTOM[0] * t)
|
||||
g = int(BG_TOP[1] * (1 - t) + BG_BOTTOM[1] * t)
|
||||
b = int(BG_TOP[2] * (1 - t) + BG_BOTTOM[2] * t)
|
||||
for x in range(SIZE):
|
||||
px[x, y] = (r, g, b)
|
||||
|
||||
# rounded-square mask
|
||||
mask = Image.new("L", (SIZE, SIZE), 0)
|
||||
ImageDraw.Draw(mask).rounded_rectangle([0, 0, SIZE - 1, SIZE - 1], radius=SIZE // 5, fill=255)
|
||||
canvas = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
|
||||
canvas.paste(img, (0, 0), mask)
|
||||
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
font = ImageFont.truetype("/usr/share/fonts/truetype/google-fonts/Poppins-Bold.ttf", int(SIZE * 0.56))
|
||||
text = "R"
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||
draw.text(((SIZE - tw) / 2 - bbox[0], (SIZE - th) / 2 - bbox[1] - SIZE * 0.02), text, font=font, fill=FG)
|
||||
|
||||
return canvas
|
||||
|
||||
master = make_master()
|
||||
master.save(os.path.join(OUT, "icon-master.png"))
|
||||
|
||||
sizes = [32, 128, 256, 512, 1024]
|
||||
@@ -62,4 +40,4 @@ try:
|
||||
except Exception as e:
|
||||
print(f"WARN: could not write icon.icns via Pillow ({e}); leaving it out")
|
||||
|
||||
print("done:", os.listdir(OUT))
|
||||
print("done:", sorted(os.listdir(OUT)))
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 774 KiB |
@@ -1,18 +1,18 @@
|
||||
[package]
|
||||
name = "renamr"
|
||||
name = "aurora-curator"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "renamr_lib"
|
||||
name = "aurora_curator_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
renamr-core = { path = "../crates/renamr-core" }
|
||||
aurora-curator-core = { path = "../crates/aurora-curator-core" }
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-dialog = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Default permissions for Renamr's main window.",
|
||||
"description": "Default permissions for Aurora Curator's main window.",
|
||||
"windows": ["main"],
|
||||
"permissions": ["core:default", "dialog:default"]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 102 KiB |
@@ -1,10 +1,10 @@
|
||||
//! The Tauri-facing side of rename history/undo: persists the log
|
||||
//! `renamr_core::history` produces to disk (plain JSON in the OS app-config
|
||||
//! `aurora_curator_core::history` produces to disk (plain JSON in the OS app-config
|
||||
//! directory, alongside `settings.json`) and exposes it to the UI as two commands -
|
||||
//! list the log, and undo one batch from it.
|
||||
|
||||
use renamr_core::history::{self, HistoryBatch, HistoryEntry, RevertResult};
|
||||
use renamr_core::rename::{RenameAction, RenameOutcome, RenameResult};
|
||||
use aurora_curator_core::history::{self, HistoryBatch, HistoryEntry, RevertResult};
|
||||
use aurora_curator_core::rename::{RenameAction, RenameOutcome, RenameResult};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@@ -45,7 +45,7 @@ fn now_millis() -> u64 {
|
||||
/// Called right after a rename batch executes (see `execute_rename` in `lib.rs`) -
|
||||
/// records only the entries that were actually renamed/copied/linked, and the action
|
||||
/// that was used to do it (so `undo_rename_batch` later knows whether to move the
|
||||
/// result back or just delete it - see `renamr_core::history::revert_one`); a file that
|
||||
/// result back or just delete it - see `aurora_curator_core::history::revert_one`); a file that
|
||||
/// was unchanged, skipped, or failed has nothing to undo. Best-effort: if persisting the
|
||||
/// history fails for some reason, the operation itself already succeeded and shouldn't
|
||||
/// be reported as failed just because its undo record couldn't be saved, so errors here
|
||||
|
||||
@@ -4,10 +4,10 @@ mod metadata;
|
||||
mod presets;
|
||||
mod settings;
|
||||
|
||||
use renamr_core::format::ExpressionFormat;
|
||||
use renamr_core::matching::{self, EpisodeGuess, MovieGuess, ReleaseInfo};
|
||||
use renamr_core::rename::{self, ConflictPolicy, RenameAction, RenamePlanItem, RenameResult};
|
||||
use renamr_core::string_bindings;
|
||||
use aurora_curator_core::format::ExpressionFormat;
|
||||
use aurora_curator_core::matching::{self, EpisodeGuess, MovieGuess, ReleaseInfo};
|
||||
use aurora_curator_core::rename::{self, ConflictPolicy, RenameAction, RenamePlanItem, RenameResult};
|
||||
use aurora_curator_core::string_bindings;
|
||||
use std::collections::HashMap;
|
||||
use tauri::AppHandle;
|
||||
|
||||
@@ -42,7 +42,7 @@ fn guess_movie(filename: String) -> Option<MovieGuess> {
|
||||
|
||||
/// Guesses release-tag metadata (resolution, source, video/audio codec, release
|
||||
/// group) from a filename's own text using local regex patterns only - no network
|
||||
/// calls, no real media probing (Renamr never ships an ffprobe/MediaInfo dependency,
|
||||
/// calls, no real media probing (Aurora Curator never ships an ffprobe/MediaInfo dependency,
|
||||
/// so filename tags are the only source for these fields). Independent of
|
||||
/// `guess_episode`/`guess_movie`: called alongside whichever of those two applies,
|
||||
/// since a release name carries (or doesn't carry) these tags regardless of whether
|
||||
@@ -53,7 +53,7 @@ fn guess_release_info(filename: String) -> Option<ReleaseInfo> {
|
||||
}
|
||||
|
||||
/// Executes a batch of planned on-disk rename/copy/symlink operations (optionally
|
||||
/// moving/copying files into subfolders - see `renamr_core::rename`). `action` picks
|
||||
/// moving/copying files into subfolders - see `aurora_curator_core::rename`). `action` picks
|
||||
/// which of the three happens; `conflict` picks what happens when a computed target
|
||||
/// already exists (skip it, save alongside it with an auto-numbered name, or replace
|
||||
/// it) - the UI is expected to have already shown the user the full old-path/new-path
|
||||
@@ -103,5 +103,5 @@ pub fn run() {
|
||||
files::expand_dropped_paths,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running Renamr");
|
||||
.expect("error while running Aurora Curator");
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
renamr_lib::run();
|
||||
aurora_curator_lib::run();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! Fetch Data needs a series name and season/episode to search with, which is exactly
|
||||
//! what Match already produces for free, offline.
|
||||
|
||||
use renamr_core::web::tmdb::{SeasonEpisode, TmdbClient};
|
||||
use aurora_curator_core::web::tmdb::{SeasonEpisode, TmdbClient};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use tauri::AppHandle;
|
||||
@@ -182,7 +182,7 @@ async fn run_movie_fetch(token: String, items: Vec<MovieFetchQuery>) -> Vec<Movi
|
||||
title: info.title,
|
||||
release_date: info.release_date,
|
||||
},
|
||||
Err(renamr_core::web::tmdb::TmdbError::NotFound(_)) => MovieFetchOutcome::NotFound,
|
||||
Err(aurora_curator_core::web::tmdb::TmdbError::NotFound(_)) => MovieFetchOutcome::NotFound,
|
||||
Err(error) => MovieFetchOutcome::Failed { error: error.to_string() },
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Small persisted app settings - currently just the TMDB API token ("Fetch Data" needs
|
||||
//! it; everything else in the app works offline without any settings at all). Stored as
|
||||
//! plain JSON in Tauri's per-OS app config directory (e.g.
|
||||
//! `~/Library/Application Support/com.renamr.app/settings.json` on macOS,
|
||||
//! `~/.config/renamr/settings.json` on Linux) - not the original FileBot's config
|
||||
//! location, since that's Java Preferences-backed and OS/JVM-specific; this is Renamr's
|
||||
//! `~/Library/Application Support/com.auroracurator.app/settings.json` on macOS,
|
||||
//! `~/.config/aurora-curator/settings.json` on Linux) - not the original FileBot's config
|
||||
//! location, since that's Java Preferences-backed and OS/JVM-specific; this is Aurora Curator's
|
||||
//! own, analogous location for its own settings.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Renamr",
|
||||
"productName": "Aurora Curator",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.renamr.app",
|
||||
"identifier": "com.auroracurator.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"beforeBuildCommand": "npm run build",
|
||||
@@ -12,7 +12,7 @@
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Renamr",
|
||||
"title": "Aurora Curator",
|
||||
"width": 1100,
|
||||
"height": 720,
|
||||
"minWidth": 720,
|
||||
|
||||
@@ -31,7 +31,7 @@ interface FileEntry {
|
||||
ext: string;
|
||||
}
|
||||
|
||||
// Result of the local, filename-only season/episode matcher (renamr-core's
|
||||
// Result of the local, filename-only season/episode matcher (aurora-curator-core's
|
||||
// `matching` module, exposed via the `guess_episode` Tauri command). No network calls -
|
||||
// this only recognizes patterns already present in the filename text itself.
|
||||
interface EpisodeGuess {
|
||||
@@ -40,7 +40,7 @@ interface EpisodeGuess {
|
||||
episode: number;
|
||||
}
|
||||
|
||||
// Result of the local, filename-only movie title/year matcher (renamr-core's
|
||||
// Result of the local, filename-only movie title/year matcher (aurora-curator-core's
|
||||
// `matching::guess_movie`, exposed via the `guess_movie` Tauri command). Tried as a
|
||||
// fallback whenever `guess_episode` finds no season/episode pattern - a release name
|
||||
// is either a TV episode or a movie, never both.
|
||||
@@ -52,7 +52,7 @@ interface MovieGuess {
|
||||
// What runMatching resolved a file to, before it's written into the item's bindings.
|
||||
type MatchGuess = { kind: "tv"; episode: EpisodeGuess } | { kind: "movie"; movie: MovieGuess };
|
||||
|
||||
// Result of the local, filename-only release-tag matcher (renamr-core's
|
||||
// Result of the local, filename-only release-tag matcher (aurora-curator-core's
|
||||
// `matching::guess_release_info`, exposed via the `guess_release_info` Tauri command).
|
||||
// Independent of the TV/movie guess above - a release name carries (or doesn't carry)
|
||||
// these tags regardless of what kind of media it is, so this runs alongside whichever
|
||||
@@ -78,7 +78,7 @@ interface RenamePlanEntry {
|
||||
newName: string;
|
||||
}
|
||||
|
||||
// Mirrors renamr-core's `RenameOutcome` (serde-tagged on "status"). Conflict policy is
|
||||
// Mirrors aurora-curator-core's `RenameOutcome` (serde-tagged on "status"). Conflict policy is
|
||||
// fixed at skip - a colliding target is never overwritten, just reported.
|
||||
type RenameOutcome =
|
||||
| { status: "renamed"; newPath: string }
|
||||
@@ -91,15 +91,15 @@ interface RenameResult {
|
||||
outcome: RenameOutcome;
|
||||
}
|
||||
|
||||
// Mirrors renamr-core's `RenameAction` - what actually happens at each computed target.
|
||||
// "move" relocates the file in place (the only action Renamr had before); "copy" and
|
||||
// Mirrors aurora-curator-core's `RenameAction` - what actually happens at each computed target.
|
||||
// "move" relocates the file in place (the only action Aurora Curator had before); "copy" and
|
||||
// "symlink" both leave the source file exactly where it is and produce a new file
|
||||
// elsewhere, which is why only "move" updates the loaded item's own path/name (see
|
||||
// confirmRename below).
|
||||
type RenameAction = "move" | "copy" | "symlink";
|
||||
|
||||
// Mirrors renamr-core's `ConflictPolicy` - what happens when a computed target already
|
||||
// exists. "skip" (the default, and the only behavior Renamr had before) leaves the
|
||||
// Mirrors aurora-curator-core's `ConflictPolicy` - what happens when a computed target already
|
||||
// exists. "skip" (the default, and the only behavior Aurora Curator had before) leaves the
|
||||
// conflicting file alone; "autoNumber" saves alongside it as "Name (1).ext" instead;
|
||||
// "override" replaces it outright (irreversible for whatever gets overwritten).
|
||||
type ConflictPolicy = "skip" | "autoNumber" | "override";
|
||||
@@ -115,7 +115,7 @@ interface FetchQuery {
|
||||
episode: number;
|
||||
}
|
||||
|
||||
// Mirrors renamr-core's `FetchOutcome` (serde-tagged on "status").
|
||||
// Mirrors aurora-curator-core's `FetchOutcome` (serde-tagged on "status").
|
||||
type FetchOutcome =
|
||||
| { status: "found"; seriesName: string; episodeTitle: string; airDate: string | null }
|
||||
| { status: "notFound" }
|
||||
@@ -135,7 +135,7 @@ interface MovieFetchQuery {
|
||||
year: number | null;
|
||||
}
|
||||
|
||||
// Mirrors renamr-core's `MovieFetchOutcome` (serde-tagged on "status").
|
||||
// Mirrors aurora-curator-core's `MovieFetchOutcome` (serde-tagged on "status").
|
||||
type MovieFetchOutcome =
|
||||
| { status: "found"; title: string; releaseDate: string | null }
|
||||
| { status: "notFound" }
|
||||
@@ -146,7 +146,7 @@ interface MovieFetchResult {
|
||||
outcome: MovieFetchOutcome;
|
||||
}
|
||||
|
||||
// Rename history / undo. Mirrors renamr-core's `history` module (serde-tagged on
|
||||
// Rename history / undo. Mirrors aurora-curator-core's `history` module (serde-tagged on
|
||||
// "status" for the outcome, same convention as RenameOutcome/FetchOutcome above).
|
||||
interface HistoryEntry {
|
||||
source: string;
|
||||
@@ -263,9 +263,9 @@ const SAMPLE_ITEMS: Item[] = [
|
||||
];
|
||||
|
||||
// The user's own preferred naming scheme (their words: "I personally like {show name} -
|
||||
// {season number}x{episode number} - {episode name}") - this is Renamr's default now,
|
||||
// {season number}x{episode number} - {episode name}") - this is Aurora Curator's default now,
|
||||
// not just an example. `{show name}`/`{season number}`/etc. are multi-word plain-field
|
||||
// binding names, which work via renamr-core's ExpressionFormat plain-field-name
|
||||
// binding names, which work via aurora-curator-core's ExpressionFormat plain-field-name
|
||||
// fallback (ExpressionError-free even though they aren't valid Rhai identifiers).
|
||||
const SAMPLE_EXPRESSION = "{show name} - {season number}x{episode number} - {episode name}";
|
||||
// The matching engine isn't ported yet, so real loaded files only have their own
|
||||
@@ -395,10 +395,10 @@ export default function RenamePanel() {
|
||||
const [renaming, setRenaming] = useState(false);
|
||||
// What to actually do at each computed target - chosen in the confirmation modal,
|
||||
// remembered across modal opens within the session (defaults to "move", matching
|
||||
// Renamr's original and only behavior before copy/symlink existed).
|
||||
// Aurora Curator's original and only behavior before copy/symlink existed).
|
||||
const [renameAction, setRenameAction] = useState<RenameAction>("move");
|
||||
// What to do when a computed target already exists - same remembered-across-opens
|
||||
// pattern as renameAction, defaults to "skip" (Renamr's original and only behavior
|
||||
// pattern as renameAction, defaults to "skip" (Aurora Curator's original and only behavior
|
||||
// before auto-number/override existed).
|
||||
const [conflictPolicy, setConflictPolicy] = useState<ConflictPolicy>("skip");
|
||||
// TMDB token state for "Fetch Data". `null` = not checked yet, `false` = checked and
|
||||
@@ -545,10 +545,10 @@ export default function RenamePanel() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Local, filename-based season/episode/movie detection (renamr-core's `matching`
|
||||
// Local, filename-based season/episode/movie detection (aurora-curator-core's `matching`
|
||||
// module). Tries `guess_episode` first; when a filename carries no season/episode
|
||||
// marker, falls back to `guess_movie` (a release name is either a TV episode or a
|
||||
// movie, never both - see the doc comment on `guess_movie` in renamr-core). Populates
|
||||
// movie, never both - see the doc comment on `guess_movie` in aurora-curator-core). Populates
|
||||
// TV bindings (`{n}`/`{s00e00}`/`{season}`/`{episode}`/etc.) or movie bindings
|
||||
// (`{n}`/`{y}`/etc.) accordingly - no network calls, no metadata lookup, so it's
|
||||
// distinct from (and much cheaper than) "Fetch Data" online matching.
|
||||
@@ -727,7 +727,7 @@ export default function RenamePanel() {
|
||||
// local state and report the outcome. Only "move" relocates the source file, so only
|
||||
// "move" updates a loaded item's own id/name/bindings to point at the new path -
|
||||
// "copy"/"symlink" leave the source exactly where it was and produce an additional
|
||||
// file elsewhere that Renamr doesn't separately track as a loaded item.
|
||||
// file elsewhere that Aurora Curator doesn't separately track as a loaded item.
|
||||
const confirmRename = async () => {
|
||||
if (!renamePlan || renamePlan.length === 0) return;
|
||||
setRenaming(true);
|
||||
@@ -805,7 +805,7 @@ export default function RenamePanel() {
|
||||
setRenamePlan((prev) => (prev ? prev.filter((p) => p.id !== id) : prev));
|
||||
};
|
||||
|
||||
// Rename history / undo - Renamr's only safety net beyond the confirm-before-execute
|
||||
// Rename history / undo - Aurora Curator's only safety net beyond the confirm-before-execute
|
||||
// step, since a filesystem rename has no OS-level undo. `execute_rename` records a
|
||||
// batch of every successfully-renamed file after each Rename; this panel lists those
|
||||
// batches and can revert one back to its original paths.
|
||||
@@ -1310,7 +1310,7 @@ export default function RenamePanel() {
|
||||
<p className="format-hint">
|
||||
Match scans filenames locally for season/episode or movie title/year patterns, plus release-tag text
|
||||
like resolution, source (BluRay/HDTV/WEB-DL/…), video/audio codec, and release group — none of this
|
||||
needs a network call, since it's all parsed straight from the filename itself (Renamr has no video/audio
|
||||
needs a network call, since it's all parsed straight from the filename itself (Aurora Curator has no video/audio
|
||||
file prober, so a tag that isn't in the filename text just won't be available). Fetch Data resolves the
|
||||
season/episode or movie guess against real TMDB metadata for the real title, episode title, and
|
||||
air/release date — needs a free TMDB API token set in <strong>TMDB Settings</strong> first. A batch can
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function Sidebar({
|
||||
</ul>
|
||||
|
||||
<div className="sidebar-footer">
|
||||
<span className="sidebar-wordmark">Renamr</span>
|
||||
<span className="sidebar-wordmark">Aurora Curator</span>
|
||||
<span className="sidebar-version">v{APP_VERSION}</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* Renamr "Aurora" theme — ported 1:1 (hex values) from the original FB-Mod fork's
|
||||
/* Aurora Curator's UI theme — ported 1:1 (hex values) from the original FB-Mod fork's
|
||||
AuroraTheme.java / HeaderPanel.java / MainFrame.java so the rewrite looks like a
|
||||
continuation of that design rather than a random restyle. */
|
||||
continuation of that design rather than a random restyle. (The app takes its name,
|
||||
"Aurora Curator", from this same Aurora theme.) */
|
||||
:root {
|
||||
--dock: #08252b;
|
||||
--dock-deep: #051115;
|
||||
|
||||