Skip to content
Draft
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
ec91193
feat: image viewer from Thorium chrome gui
panaC Feb 9, 2025
abde71f
Merge branch 'develop' into feat/image-ai
panaC Feb 13, 2025
304ead2
works with openai gpt4o-mini
panaC Feb 13, 2025
6458089
Merge branch 'develop' into feat/image-click
panaC Feb 13, 2025
be50812
Merge branch 'feat/image-click' into feat/image-ai
panaC Feb 13, 2025
8edac42
pass img buffer from main process instead of base64 dataURL from rend…
panaC Feb 13, 2025
4e3de86
add model selection (openai/mistralai) and systemPrompt
panaC Feb 13, 2025
6560f0e
Merge branch 'develop' into feat/image-click
panaC Feb 14, 2025
e7f5f1a
Merge branch 'feat/image-click' into feat/image-ai
panaC Feb 14, 2025
0fbfee1
add .env in gitignore
panaC Feb 14, 2025
90fcdbc
add dotenv debug config
panaC Feb 14, 2025
38dec4c
fix ui & state persistence
panaC Feb 14, 2025
4ff6dbd
first draft ui design
arthur-lemeur Feb 18, 2025
d4e5f82
Merge branch 'develop' into feat/image-click
danielweck Feb 18, 2025
7a07406
Merge branch 'feat/image-click' into feat/image-ai
danielweck Feb 18, 2025
fadd73b
Merge branch 'develop' into feat/image-click
danielweck Feb 18, 2025
71b33ba
Merge branch 'feat/image-click' into feat/image-ai
danielweck Feb 18, 2025
30f1544
Merge branch 'develop' into feat/image-click
danielweck Feb 20, 2025
b67d3ab
Merge branch 'feat/image-click' into feat/image-ai
danielweck Feb 20, 2025
66e2b15
Merge branch 'develop' into feat/image-ai
danielweck Feb 22, 2025
4be6e2f
NPM package updates
danielweck Feb 22, 2025
c27be39
Merge branch 'develop' into feat/image-ai
danielweck Feb 23, 2025
44750fa
Merge branch 'develop' into feat/image-ai
panaC Feb 24, 2025
40fcf13
feat: img context and accessibility parsing from dom
panaC Feb 24, 2025
e16cea9
Merge branch 'develop' into feat/image-ai
danielweck Feb 26, 2025
cac192b
Merge branch 'develop' into feat/image-ai
danielweck Mar 2, 2025
bf0582d
Merge branch 'develop' into feat/image-ai
danielweck Mar 3, 2025
f1e69fe
Merge branch 'develop' into feat/image-ai
danielweck Mar 4, 2025
98cea84
up chatbot dialog and system prompt
arthur-lemeur Mar 6, 2025
4ba3dfe
add api key section in settings (frontend only)
arthur-lemeur Mar 6, 2025
8b700cd
Merge branch 'develop' into feat/image-ai
danielweck Mar 7, 2025
94ed44d
npm packages
danielweck Mar 7, 2025
553f023
Merge branch 'develop' into feat/image-ai
danielweck Mar 8, 2025
9e61add
Merge branch 'develop' into feat/image-ai
danielweck Mar 10, 2025
237d8dd
Merge branch 'develop' into feat/image-ai
danielweck Mar 10, 2025
5169948
Merge branch 'develop' into feat/image-ai
danielweck Mar 14, 2025
468a8f8
NPM package udpates
danielweck Mar 14, 2025
2742100
update image description dialog to first version of UI design
arthur-lemeur Mar 20, 2025
d2ac95f
up and clean styles
arthur-lemeur Mar 20, 2025
8b6d2f2
up icons and brand name in add api key settings
arthur-lemeur Mar 20, 2025
b65d4bd
up css
arthur-lemeur Mar 24, 2025
9bb1d3d
add api keys to redux state and persistence
arthur-lemeur Mar 25, 2025
58c5527
up frontend relative to api keys
arthur-lemeur Mar 25, 2025
e939e8c
Merge branch 'develop' into feat/image-ai
arthur-lemeur Mar 27, 2025
89546cc
up AiKeyManager component in settings
arthur-lemeur Mar 31, 2025
6d56d3a
up redux actions aiApiKey
arthur-lemeur Apr 1, 2025
205e132
Merge branch 'develop' into feat/image-ai
arthur-lemeur Apr 1, 2025
830e05f
up aiApiKeys reducer and naming
arthur-lemeur Apr 2, 2025
c8eac63
fix aiApiKeys reducer
arthur-lemeur Apr 3, 2025
5e382fe
up AiKeyCard (localized steps, input behavior)
arthur-lemeur Apr 3, 2025
22ca998
up ai keys reducer
arthur-lemeur Apr 3, 2025
7b7be88
Merge branch 'develop' into feat/image-ai
arthur-lemeur Apr 14, 2025
1f01566
fix CI
arthur-lemeur Apr 14, 2025
cb1d177
up
arthur-lemeur Apr 14, 2025
8e54507
Merge branch 'develop' into feat/image-ai
panaC May 27, 2025
155cf97
Merge branch 'develop' into feat/image-ai
panaC May 27, 2025
3ebd1a1
Merge branch 'develop' into feat/image-ai
panaC May 27, 2025
b78e981
Merge branch 'develop' into feat/image-ai
panaC Jun 5, 2025
eac8d0c
fixes previous merge 'resourceCache'
panaC Jun 5, 2025
dc9b5d7
fix: advanced system prompt
panaC Jun 5, 2025
8c64220
fix modelSelected select component
panaC Jun 5, 2025
ecf5a26
Merge branch 'develop' into feat/image-ai
panaC Jun 11, 2025
c67943f
fix: markdown href
panaC Jun 11, 2025
4267641
fix(apikeys): .env initilialisation
panaC Jun 11, 2025
18fd0b9
gemini
panaC Jun 12, 2025
265ba4e
fix add search selection
panaC Jun 12, 2025
856ba13
i18n en: extended description
panaC Jun 12, 2025
efcb360
Merge branch 'develop' into feat/image-ai
danielweck Jun 14, 2025
1fe25a2
Merge branch 'develop' into feat/image-ai
danielweck Jun 14, 2025
f04886b
tweaked prompts and GUI
danielweck Jun 15, 2025
806a60d
fixes
danielweck Jun 15, 2025
83e3741
tweaks
danielweck Jun 15, 2025
36bcb7a
autoscroll to bottom works again
danielweck Jun 15, 2025
cb0245e
typo
danielweck Jun 15, 2025
a66b17e
Merge branch 'develop' into feat/image-ai
panaC Jun 24, 2025
4d97dc2
up
panaC Jun 24, 2025
e786c21
AI FEATURE FLAG
panaC Jun 24, 2025
07c188c
lint: require
panaC Jun 24, 2025
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
304 changes: 301 additions & 3 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@
}
},
"dependencies": {
"@ai-sdk/mistral": "^1.1.17",
"@ai-sdk/openai": "^1.2.5",
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-popover": "^1.1.6",
Expand All @@ -278,6 +280,7 @@
"@react-hook/resize-observer": "^2.0.2",
"@redux-saga/core": "^1.3.0",
"@xmldom/xmldom": "^0.9.8",
"ai": "^4.1.61",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"classnames": "^2.5.1",
Expand All @@ -293,6 +296,7 @@
"jsdom": "^26.0.0",
"lunr": "^2.3.9",
"lunr-languages": "^1.14.0",
"marked": "^15.0.7",
"match-sorter": "^8.0.0",
"mathjax": "^3.2.2",
"mime-types": "^2.1.35",
Expand Down
40 changes: 40 additions & 0 deletions src/common/aisdkModelOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

export const DEFAULT_SYSTEM_PROMPT = "Your goal is to describe the image, you should not answer on a topic other than this image. Answer all requests in {{languages}} unless I explicitly ask you otherwise.";
const ADVANCED_SYSTEM_PROMPT = {
goal: "describe the image, you should not answer on a topic other than this image. Answer all requests in {{languages}} unless I explicitly ask you otherwise",
context: {
title: "{{title}}",
author: "{{author}}",
publisher: "{{publisher}}",
languages: "{{languages}}",
text_before: "{{beforeText}}",
text_after: "{{afterText}}",
},
};

const ADVANCED_SYSTEM_PROMPT_STRING = JSON.stringify(ADVANCED_SYSTEM_PROMPT);

export interface IaiSdkModel { id: string, name: string, systemPrompt: string };
export const aiSDKModelOptions: Array<IaiSdkModel> = [
{
id: "openai__!__gpt-4o-mini__!__default-prompt",
name: "openAI gpt-4o-mini (default)",
systemPrompt: DEFAULT_SYSTEM_PROMPT,
},
{
id: "openai__!__gpt-4o-mini__!__specific-prompt",
name: "openAI gpt-4o-mini (advanced)",
systemPrompt: ADVANCED_SYSTEM_PROMPT_STRING,
},
{
id: "mistralai__!__pixtral-12b-2409",
name: "mistralAI Pixtral 12B",
systemPrompt: DEFAULT_SYSTEM_PROMPT,
},
];
15 changes: 15 additions & 0 deletions src/common/redux/actions/api_key/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

// import * as enable from "./enable";
import * as set from "./setKey";
import * as removeKey from "./removeKey";

export {
set,
removeKey,
};
27 changes: 27 additions & 0 deletions src/common/redux/actions/api_key/removeKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

import { Action } from "readium-desktop/common/models/redux";
import { IApiKey } from "../../states/api_key";

export const ID = "API_KEY_REMOVE";

// eslint-disable-next-line @typescript-eslint/no-empty-interface
// interface IPayload extends Pick<IAnnotationState, "uuid"> {
interface IPayload extends IApiKey {
}

export function build(param: IApiKey):
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just need the api provider name identifier in parameter not the entire IApiKey interface, but why not

Action<typeof ID, IPayload> {

return {
type: ID,
payload: {...param},
};
}
build.toString = () => ID;
export type TAction = ReturnType<typeof build>;
28 changes: 28 additions & 0 deletions src/common/redux/actions/api_key/setKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

import { Action } from "readium-desktop/common/models/redux";
import { IApiKey } from "../../states/api_key";

export const ID = "API_KEY_SET";

export interface Payload extends IApiKey {
}

export function build(key: string, provider: string, submitted: boolean): Action<typeof ID, Payload> {

return {
type: ID,
payload: {
key,
provider,
submitted,
},
};
}
build.toString = () => ID;
export type TAction = ReturnType<typeof build>;
2 changes: 2 additions & 0 deletions src/common/redux/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as versionUpdateActions from "./version-update";
import * as annotationActions from "./annotation";
import * as creatorActions from "./creator";
import * as settingsActions from "./settings";
import * as apiKeysActions from "./api_key";
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api_key is not explicit, we need a better name to identify that this is to store the api key for AI provider


export {
historyActions,
Expand All @@ -53,4 +54,5 @@ export {
annotationActions,
creatorActions,
settingsActions,
apiKeysActions,
};
40 changes: 40 additions & 0 deletions src/common/redux/reducers/api_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

import { type Reducer } from "redux";

import { apiKeysActions } from "../actions";
import { IApiKeysArray } from "../states/api_key";

const initialState: IApiKeysArray = [];

function apiKeysReducer_(
state = initialState,
action: apiKeysActions.set.TAction | apiKeysActions.removeKey.TAction,
): IApiKeysArray {

switch (action.type) {
case apiKeysActions.set.ID:
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that this is more an add/update that an infinite push to an array

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And in this case, remove is not needed, it's more a reset feature for the key of the API AI Provider than a total remove, api providers is known and fixed

return [
...state,
{
provider: action.payload.provider,
key: action.payload.key,
submitted: action.payload.submitted,
},
];
case apiKeysActions.removeKey.ID:
return state.filter(
key => key.provider !== action.payload.provider || key.key !== action.payload.key,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about the same key for several api provider ?

);
default:
return state;
}
}


export const apiKeysReducer = apiKeysReducer_ as Reducer<ReturnType<typeof apiKeysReducer_>>;
14 changes: 14 additions & 0 deletions src/common/redux/states/api_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

export interface IApiKey {
key: string,
provider: string,
submitted: boolean,
}

export type IApiKeysArray = IApiKey[]
2 changes: 2 additions & 0 deletions src/common/redux/states/commonRootState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { INoteCreator } from "./creator";
import { I18NState } from "readium-desktop/common/redux/states/i18n";
import { TFIFOState } from "readium-desktop/utils/redux-reducers/fifo.reducer";
import { IAnnotationPreParsingState } from "./renderer/annotation";
import { IApiKeysArray } from "readium-desktop/common/redux/states/api_key";

export interface ICommonRootState {
i18n: I18NState;
Expand All @@ -28,4 +29,5 @@ export interface ICommonRootState {
theme: ITheme;
creator: INoteCreator;
annotationImportQueue: TFIFOState<IAnnotationPreParsingState>;
apiKeys: IApiKeysArray;
}
2 changes: 2 additions & 0 deletions src/main/redux/middleware/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const reduxPersistMiddleware: Middleware
settings: prevState.settings,
creator: prevState.creator,
annotationImportQueue: prevState.annotationImportQueue,
apiKeys: prevState.apiKeys,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not persist apiKeys to the redux state but instead in a separate encrypted json file, I think

};

const persistNextState: PersistRootState = {
Expand All @@ -64,6 +65,7 @@ export const reduxPersistMiddleware: Middleware
settings: nextState.settings,
creator: nextState.creator,
annotationImportQueue: nextState.annotationImportQueue,
apiKeys: nextState.apiKeys,
};

// RangeError: Maximum call stack size exceeded
Expand Down
5 changes: 4 additions & 1 deletion src/main/redux/middleware/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
publicationActions, themeActions,
readerActions, sessionActions, toastActions, versionUpdateActions,
creatorActions,
annotationActions,
annotationActions, apiKeysActions,
} from "readium-desktop/common/redux/actions";
import { ActionSerializer } from "readium-desktop/common/services/serializer";
import { getLibraryWindowFromDi, getReaderWindowFromDi } from "readium-desktop/main/di";
Expand Down Expand Up @@ -94,6 +94,9 @@ const SYNCHRONIZABLE_ACTIONS: string[] = [
annotationActions.shiftFromAnnotationImportQueue.ID,

readerActions.setTheLock.ID,

apiKeysActions.set.ID,
apiKeysActions.removeKey.ID,
];

export const reduxSyncMiddleware: Middleware
Expand Down
3 changes: 2 additions & 1 deletion src/main/redux/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { creatorReducer } from "readium-desktop/common/redux/reducers/creator";
import { settingsReducer } from "readium-desktop/common/redux/reducers/settings";
import { fifoReducer } from "readium-desktop/utils/redux-reducers/fifo.reducer";
import { IAnnotationPreParsingState } from "readium-desktop/common/redux/states/renderer/annotation";
import { apiKeysReducer } from "readium-desktop/common/redux/reducers/api_key";

export const rootReducer = combineReducers({ // RootState
versionUpdate: versionUpdateReducer,
Expand Down Expand Up @@ -122,5 +123,5 @@ export const rootReducer = combineReducers({ // RootState
},
},
),

apiKeys: apiKeysReducer,
});
1 change: 1 addition & 0 deletions src/main/redux/sagas/persist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const persistStateToFs = async (nextState: RootState) => {
settings: nextState.settings,
creator: nextState.creator,
annotationImportQueue: nextState.annotationImportQueue,
apiKeys: nextState.apiKeys,
};

await fsp.writeFile(stateFilePath, JSON.stringify(value), {encoding: "utf8"});
Expand Down
1 change: 1 addition & 0 deletions src/main/redux/sagas/win/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function* winOpen(action: winActions.library.openSucess.TAction) {
},
creator: state.creator,
settings: state.settings,
apiKeys: state.apiKeys,
};
try {
const publication = yield* callTyped(getCatalog);
Expand Down
2 changes: 2 additions & 0 deletions src/main/redux/sagas/win/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ function* winOpen(action: winActions.reader.openSucess.TAction) {
const transientConfigMerge = {...readerConfigInitialState, ...config};
const creator = yield* selectTyped((_state: RootState) => _state.creator);
const annotationImportQueue = yield* selectTyped((_state: RootState) => _state.annotationImportQueue);
const apiKeys = yield* selectTyped((_state: RootState) => _state.apiKeys);

const publicationRepository = diMainGet("publication-repository");
let tag: string[] = [];
Expand Down Expand Up @@ -109,6 +110,7 @@ function* winOpen(action: winActions.reader.openSucess.TAction) {
tag,
},
annotationImportQueue,
apiKeys,
},
} as readerIpc.EventPayload);
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/redux/states/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { IDictWinSessionReaderState } from "./win/session/reader";
import { ICommonRootState } from "readium-desktop/common/redux/states/commonRootState";
import { IWizardState } from "readium-desktop/common/redux/states/wizard";
import { ISettingsState } from "readium-desktop/common/redux/states/settings";
import { IApiKeysArray } from "readium-desktop/common/redux/states/api_key";

export interface RootState extends ICommonRootState {
app: AppState;
Expand Down Expand Up @@ -51,6 +52,7 @@ export interface RootState extends ICommonRootState {
version: string;
wizard: IWizardState;
settings: ISettingsState;
apiKeys: IApiKeysArray;
}

export type PersistRootState = Pick<RootState, "win" | "publication" | "reader" | "session" | "i18n" | "opds" | "version" | "theme" | "wizard" | "settings" | "creator" | "annotationImportQueue">;
export type PersistRootState = Pick<RootState, "win" | "publication" | "reader" | "session" | "i18n" | "opds" | "version" | "theme" | "wizard" | "settings" | "creator" | "annotationImportQueue" | "apiKeys">;
Loading