Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2bce47f
Add dependecies
RafaUC Sep 27, 2025
7253a86
Mak has deprecated the previous backend.
RafaUC Sep 27, 2025
0e6edcb
Set up compatible better-sqlite3 version and configure Kysely manual …
RafaUC Sep 28, 2025
f6bf5e2
Create SQL schema, table migrations, and kysely typeScript types
RafaUC Oct 3, 2025
1cda3e5
Created import from old JSON format, migrated backup data, and fixed …
RafaUC Oct 11, 2025
161a987
WIP Creating Backend Class: Fetch methods
RafaUC Oct 20, 2025
c861a11
Created Backend class
RafaUC Nov 4, 2025
62af34b
Implement the BackupScheduler class and refactor some DB import methods.
RafaUC Nov 10, 2025
1a9f520
Clean up commented lines, and remove Dexie dependencies.
RafaUC Nov 13, 2025
a122c01
Change all advanced search components to support criteria conjunctions.
RafaUC Nov 18, 2025
367dc27
- Feature: Create advanced searches with nested conjunctions or subqu…
RafaUC Dec 28, 2025
e1416c8
Fix proxy backupScheduler funtions to Backend in worker mode
RafaUC Dec 29, 2025
41f09fb
Implement the backup-scheduler to work as a worker, fix import and mi…
RafaUC Jan 2, 2026
d75cb14
Features: Implement pagination in fetching file data and batch proces…
RafaUC Jan 26, 2026
1815b29
Merge branch 'master' of github.com:RafaUC/Allusion into migration/db…
RafaUC Jan 26, 2026
8e53410
remove black ring animation when deselecting files
RafaUC Jan 27, 2026
44dccfc
- Implemented select all files support and operations for files not l…
RafaUC Feb 4, 2026
1b3456a
- Fixed a bug where if saving a tag with too many subtags the query m…
RafaUC Feb 6, 2026
a6f67a3
fix: configure better-sqlite3 as external and include dependencies
RafaUC Feb 6, 2026
b7f6c50
change workflows node version
RafaUC Feb 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v1
with:
node-version: 16
node-version: 22

- name: Build/release Electron app
uses: samuelmeuli/action-electron-builder@v1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v1
with:
node-version: 16
node-version: 22

- name: Get yarn cache directory path
id: yarn-cache-dir-path
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
- name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v1
with:
node-version: 16
node-version: 22
github_token: ${{ secrets.github_token }}

- name: Get yarn cache directory path
Expand Down
17 changes: 17 additions & 0 deletions common/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,20 @@ export function normalizeBase(str: string): string {
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase();
}

/** Returns the date at 00:00 today */
export function getToday(): Date {
const today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0, 0);
return today;
}

/** Returns the date at the start of the current week (Sunday at 00:00) */
export function getWeekStart(): Date {
const date = getToday();
const dayOfWeek = date.getDay();
date.setDate(date.getDate() - dayOfWeek);
return date;
}
22 changes: 22 additions & 0 deletions src/frontend/TimingManager.ts → common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,25 @@ export class TimingManager {
this.timings.clear();
}
}

export const debugShallowCompare = <T extends Record<string, any>>(
componentName = 'MemoComponent',
) => {
return (prev: T, next: T): boolean => {
let areEqual = true;

const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);

for (const key of keys) {
if (!Object.is(prev[key], next[key])) {
areEqual = false;
console.log(`[${componentName}] prop changed → "${key}"`, {
from: prev[key],
to: next[key],
});
}
}

return areEqual;
};
};
49 changes: 49 additions & 0 deletions common/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,52 @@ export function promiseAllLimit<T>(

return pendingPromise;
}

export type BatchFetcher<T, NextOpts> = (opts?: NextOpts) => Promise<{
items: T[];
nextOpts?: NextOpts;
}>;

export type BatchProcessor<T, Acc> = (batch: T[], acc: Acc) => Promise<Acc> | Acc;

/**
* Iterates over paginated resources and applies a processing function to each batch.
* @template T - The type of items retrieved.
* @template NextOpts - The type of the cursor for pagination or any options to compute the next batch.
* @template Acc - The type of the result accumulator.
* @param fetchBatch - Function to retrieve the next batch of data.
* @param processBatch - Function to apply logic to the current batch and update the accumulator.
* @param initialAcc - The starting value for the accumulation.
* @param cancel - Optional callback to abort the process prematurely.
* @returns A promise that resolves to the final accumulated value.
*/
export async function batchReducer<T, NextOpts, Acc>(
fetchBatch: BatchFetcher<T, NextOpts>,
processBatch: BatchProcessor<T, Acc>,
initialAcc: Acc,
cancel?: () => boolean,
): Promise<Acc> {
let opts: NextOpts | undefined;
let acc = initialAcc;

while (true) {
if (cancel?.()) {
console.log('CANCELLING!');
break;
}
const { items, nextOpts } = await fetchBatch(opts);

if (!items.length) {
break;
}

acc = await processBatch(items, acc);

if (!nextOpts) {
break;
}
opts = nextOpts;
}

return acc;
}
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"package": "yarn build && electron-builder",
"logo": "ncp ./resources/logo/icns/allusion-logomark-fc.icns ./build/icon.icns && ncp ./resources/logo/ico/allusion-logomark-fc-256x256.ico ./build/icon.ico",
"build": "rimraf dist && yarn production && yarn logo",
"postinstall": "electron-rebuild -f -w better-sqlite3",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"build:masonry": "cd wasm/wasm-build && cargo run masonry masonry/masonry-scalar && cargo run masonry masonry/masonry-simd -- -C target-feature=+simd128",
Expand Down Expand Up @@ -77,6 +78,11 @@
"node_modules/picomatch/**/*",
"node_modules/node-addon-api/**/*",
"node_modules/is-extglob/**/*",

"node_modules/better-sqlite3/**/*",
"node_modules/bindings/**/*",
"node_modules/file-uri-to-path/**/*",

"build/**/*",
"package.json"
],
Expand All @@ -94,6 +100,7 @@
"homepage": "https://github.com/RafaUC/Allusion/Allusion#readme",
"devDependencies": {
"@svgr/webpack": "^6.5.1",
"@types/better-sqlite3": "^7.6.13",
"@types/chrome": "^0.0.195",
"@types/fs-extra": "^11.0.1",
"@types/jest": "^29.5.1",
Expand All @@ -107,6 +114,7 @@
"css-loader": "^6.7.3",
"electron": "21.3.0",
"electron-builder": "23.6.0",
"electron-rebuild": "^3.2.9",
"eslint": "^8.34.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",
Expand Down Expand Up @@ -135,11 +143,12 @@
"@floating-ui/react-dom": "^1.3.0",
"@parcel/watcher": "^2.5.1",
"ag-psd": "^15.0.0",
"better-sqlite3": "9.6.0",
"chokidar": "^3.5.3",
"comlink": "^4.4.1",
"dexie": "^3.2.3",
"dexie-export-import": "^1.0.3",
"electron-updater": "^5.3.0",
"fs-extra": "^11.1.0",
"kysely": "^0.28.7",
"mobx": "^6.8.0",
"mobx-react-lite": "^3.4.0",
"node-exiftool": "^2.3.0",
Expand Down
100 changes: 84 additions & 16 deletions resources/style/advanced-search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
display: flex;
flex-direction: column;
// changed from min-width since large tag names expand the dialog width which looks bad
width: min(85ch, 80vw);
width: min(100ch, 80vw);
margin-inline: auto;

.criteria-input,
Expand All @@ -19,46 +19,114 @@
}
}

#criteria-builder-label {
display: flex;
padding-bottom: 0.4rem;
}

#criteria-builder {
display: grid;
grid-template-columns: 1fr 1fr 1fr min-content;
grid-template-columns: 3.5rem 3fr 3fr 4fr min-content;
gap: 0.25rem 0.5rem;
align-items: center;

label {
text-transform: uppercase;
font-size: smaller;
padding-left: 1ch;
align-self: end;
}
}

#query-editor-container {
overflow: hidden auto;
padding: 2px 0;
padding: 2px;
// dialog height - height of basically everything except the container
max-height: calc(80vh - 17.25rem);
}

#query-editor {
#query-editor-container-label {
display: flex;
padding-block: 0.4rem;
}

.query-editor {
display: grid;
grid-template-columns: 3.5rem 3fr 3fr 4fr min-content;
gap: 0.25rem 0.5rem;
align-items: center;
width: 100%;
border-spacing: 0;

tr {
margin-bottom: 0.25rem;
.criteria-input,
[role='combobox'] {
//min-width: 175px;
width: 100%;
}

.group-containter {
grid-column: 1 / -1;
padding: 0.5rem;
border: 1px dashed var(--background-color-selected);
border-radius: 0.25rem;
}

.separator-line,
.criteria-separator,
.separator {
grid-column: 1 / -1;
display: flex;
align-items: center;
text-align: center;
color: var(--text-color-muted);
opacity: 0.5;

&::before,
&::after {
content: "";
flex: 1;
height: 1px;
margin: 0 8px;
}

.conjunction-input {
width: fit-content;
background: var(--background-color);
}
}

td,
th {
padding: 0;
padding-bottom: 0.25rem;
.separator-line {
&::before,
&::after {
background: var(--text-color-muted);
}
}

td {
padding-inline-start: 0.5rem;
.criteria-separator {
margin: -2.5px;
font-size: 0.55rem;

.conjunction-input {
height: 0.78rem;
min-height: unset;
}
}

.criteria-input,
[role='combobox'] {
min-width: 175px;
width: 100%;
.group-controls {
display: contents;

.btn-icon,
.criteria-input {
height: 1.1rem !important;
min-height: unset !important;
margin-bottom: 2px;
overflow: visible;
color: var(--accent-color-orange);
}

// shiulw be 3 cell width
.group-name-input {
grid-column: 2 / 5;
}
}
}
2 changes: 2 additions & 0 deletions resources/style/app-toolbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
justify-content: stretch;

.toolbar-button {
flex-shrink: 0;

&[aria-pressed='true'],
&[aria-checked='true'] {
color: var(--text-color-strong);
Expand Down
7 changes: 5 additions & 2 deletions resources/style/content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@
background-color: var(--accent-color-yellow);

:is(img, video) {
clip-path: inset(0.175rem round calc(var(--thumbnail-radius) - 0.175rem));
clip-path: inset(0.2rem round calc(var(--thumbnail-radius) - 0.2rem));
}
}
[aria-selected=true] > & > :is(img, video, .image-error){
clip-path: inset(0.175rem round calc(var(--thumbnail-radius) - 0.175rem));
clip-path: inset(0.2rem round calc(var(--thumbnail-radius) - 0.2rem));
}
:not([aria-selected=true]) > & > :is(img, video, .image-error){
transition: all 0ms;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/api/data-backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface DataBackup {
schedule(): void;
backupToFile(path: string): Promise<void>;
restoreFromFile(path: string): Promise<void>;
restoreEmpty(): Promise<void>;
peekFile(path: string): Promise<[numTags: number, numFiles: number]>;
}
15 changes: 15 additions & 0 deletions src/api/data-storage-search.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ID } from './id';

export type PropertyKeys<T> = {
[K in keyof T]: K extends string ? K : never;
}[keyof T];
Expand All @@ -13,8 +15,18 @@ export const enum OrderDirection {
Desc,
}

export type IndexableType = number | string | Date | Array<number | string | Date> | Uint8Array;

export type PaginationDirection = 'after' | 'before';

export type Cursor = { id: ID; orderValue: number | string | bigint | null };

// General search criteria for a database entity
// FFR: Boolean keys are not supported in IndexedDB/Dexie - must store booleans as 0/1
export type ConditionGroupDTO<T> = {
conjunction: SearchConjunction;
children: Array<ConditionGroupDTO<T> | ConditionDTO<T>>;
};

export type ConditionDTO<T> =
| ArrayConditionDTO<T, any>
Expand Down Expand Up @@ -53,6 +65,9 @@ export type BaseIndexSignature = { [key: string]: any };

// Trick for converting array to type https://stackoverflow.com/a/49529930/2350481

export const SearchConjunctions = ['and', 'or'] as const;
export type SearchConjunction = (typeof SearchConjunctions)[number];

export const NumberOperators = [
'equals',
'notEqual',
Expand Down
Loading
Loading