Files
readest/apps/readest-app/src/styles/globals.css
T
Mohammed Efaz 5bbc5ceccc feat(ai): AI reading assistant phase 2 (#3023)
* feat(ai): add dependencies

* chore: bump zod version to default version

* feat(ai): define types and model constants

* feat(ai): ollama provider for local LLM

* feat(ai): implement openrouter provider for cloud models

* feat(settings): register ai settings panel in global dialog

* refactor(ai): expose provider factory and service layer entry point

* test(ai): add unit tests for the providers

* test(ai): add unit tests for the providers

* feat(ai): settings panel for ai configurations

* refactor(ai): rewrite aipanel with autosave and greyed out disabled state

* fix: remove unused onClose prop from aipanel

* test(ai): update mock data

* refactor(ai): remove models

* refactor: use centralised defaults in system defaults

* chore(ai): remove comments

* fix(ai): merge default ai settings on load to prevent undefined values

* refactor(ai): rewrite settings panel with autosave and model input

* feat(ai): add ai tab with simplified highlighting

* feat(sidebar): render AIAssistant for ai tab

* feat(ai): add chat UI

* feat(ai); add chat service with RAG context

* feat(ai): temp debug logger

* feat(ai): add RAG service

* feat(ai): add text chunking utility

* feat(ai): add structured method

* feat(ai): add chatstructured method

* feat(ai): add rag types nd structured output schema

* feat(ai): add aistore, indexdb, bm25

* fix: update lock file

* feat(ai): update types for AI SDK v5

* feat(ai): add placeholder gateway model constants

* refactor(ai): update OllamaProvider for AI SDK

* feat(ai): add native gateway provider

* refactor(ai): update provider exports

* refactor(ai): use streamText from AI SDK

* refactor(ai): use embed from AI sdk

* refactor(ai): update provider factory exports

* feat(ai): add AI Elements and shadcn components

* config: add shadcn component config

* deps: add AI SDK and AI Elements dependencies

* config: add ai packages to transpilePackages

* refactor(ai): remove OpenRouterProvider and old tests

* feat(ai):add assistant-ui components

* feat(ai): add TauriChatAdapter for assistant-ui runtime

* refactor(ai): remove ai-elements components

* dep(ai): install assistant-ui and update next config

* chore(ai): export adapters from service index

* feat(ui): enhance ui components for assistant integration

* feat(settings): migrate ai settings to gateway

* feat(sidebar): integrate assistant-ui

* feat: add ai settings toggle to sidebar content

* feat: conditionally show ai tab in sidebar navigation

* feat: update ai model constants for cheaper options

* feat: add gateway provider with proxied embedding

* feat: add timeouts to ollama provider health checks

* feat: add retry logic to rag service embeddings

* feat: add error recovery to ai store

* feat: add ai feature tests

* feat: add ai api endpoints

* feat: add proxied gateway embedding provider

* feat: add ai runtime utilities

* feat: add ai retry utilities

* feat: add tauri env example template

* feat: add web env example template

* chore: add env

* feat(ai): update models and pricing, remove GLM-4.7-FlashX

* feat(ai): improve system prompt with official headings and no numeric citations

* feat(ai): optimize system prompt for tauri chat

* feat(ui): refine ai chat UI and relocate sources

* feat(ui): update ai settings panel with model pricing and custom model support

* feat(ai): add custom model support to ai settings

* test(ai): update constants tests for removed model

* feat(api): implement ai chat proxy route

* feat(api): implement ai embedding proxy route

* feat(ai): implement ai gateway health check and proxy logic

* feat(ai): simplify proxied embedding provider

* feat(ui): improve markdown text rendering

* feat(ui): add input group component

* test(ai): update ai provider tests

* feat(ai): add pageNumber to text chunk schema

* feat(ai): implement page-based chunking with 1500 char formula

* feat(ai): bump db to v2 and add store reset migration

* feat(ai): transition rag pipeline to page level spoiler filtering

* feat(ai): overhaul readest persona and antijailbreak prompt

* feat(ai): update tauri adapter for page tracking and persona

* chore(ai): export aiStore and logger from core index

* feat(reader): integrate page tracking and manual index reset

* feat(ui): add re-index action and reset logic to chat

* chore: sync pnpm lockfile with ai dependencies

* feat(utils): add browser-safe file utilities for web builds

* refactor(utils): use dynamic tauri fs import to prevent web crashes

* refactor(services): defer osType call to init() for web compatibility

* refactor(services): import RemoteFile from file.web

* refactor(services): import ClosableFile from file.web

* fix(libs): cast Entry to any for getData access

* fix(annotator): cast Overlayer to any for bubble access

* refactor(ai): replace SparklesIcon with BookOpenIcon for index prompt

* test(ai): add pageNumber to TextChunk mocks

* test(ai): fix chunkSection signature in tests

* chore: update files

* fix(ai): prevent useLocalRuntime crash when adapter is null

* refactor: optimize annotator overlay drawing

* feat: stabilize AI assistant runtime and adapter

* refactor: improve document zip loader type safety

* feat: update tauri chat adapter for dynamic options

* fix: restore architecture comments and refine platform properties

* build: update lockfile with assistant-ui patch

* fix(library): patch @assistant-ui/react for runtime initialization

* build: update dependencies in readest-app

* build: update root dependencies and patch configuration

* fix(ai): patch @assistant-ui/react for thread deletion and runtime init

* fix(ai): update assistant-ui patch with dist guards and deletion fallback

* build: sync lockfile with assistant-ui patch updates

* chore(env): update .gitignore by removing .env files from it

* chore(env): update .gitignore by adding .env.local

* chore(env): update .gitignore by adding .env*.local

* fix: restore static osType import

* chore: sync submodules with upstream/main

* refactor: remove redundant file.web module and revert import

* chore: update pnpm-lock.yaml

* refactor: revert guards

* refactor; remove deprecated codes and extract prompts.ts

* refactor(ai): remove unused ragservice exports

* refactor: remove unused ollama and embedding models

* refactor: remove unused type

* test: remove test for the now deleted constants

* refactor: remove unused export

* style: fix ui component formatting

* style: fix core and style file formatting

* test: fix broken ai provider import

* fix: typescript error

* fix: add eslint disable command

* fix(deps): remove unused ai sdk provider util after v6 ai sdk migration

* fix(patch): add lookbehind regex patch

* feat(dep): upgrade vercel ai sdk to v6 and ai-sdk-ollama to v3

* chore: update lockfile for vercel ai sdk v6

* refactor(ai): remove EmbeddingModel generic for ai sdk v6

* refactor(ai): remove EmbeddingModel generic for ai sdk v6

* test(ai): update mock to use embeddingModel

* fix(patch): add lookbehind regex patch for email autolinks in markdown

* refactor(ai): use ai sdk v6 syntax

* fix: prettier formatting

* chore: revert cargo.lock

* fix(ai): update proxied embedding model to v3 spec

* feat(ai): add aiconversation types for chat persistence

* feat(ai): add conversation/message indexeddb and crud operations

* feat(ai): create aiChatStore zustand store for chat state management

* feat(notebook): add notebookactivetab state for Notes/AI

* refactor(ai): refine conversation and message types for persistence

* feat(types): add notebookActiveTab to ReadSettings type

* chore: update deps

* feat: add notebookactive tab default value

* feat: add hook for ai chat

* feat: update left side panel with history/chat icon

* feat: integrate ChatHistoryView into sidebar content

* feat: create UI for managing AI chat history

* feat: implement persistent history with assistant-ui adapter

* feat: create tab navigation component for notes and AI

* feat: add tab navigation and AI assistant view

* feat: update header to display active tab title

* fix: formatting

* feat: remove title and update new chat button

* fix: formatting

* fix: revert tooltip and styling

* feat: implement cross-platform ask dialog bridge

* feat(ai): preserve history during ui clear & use native dialogs

* fix: align notebook navigation height with sidebar tabs

* fix(ai): add missing dependency to handleDeleteConversation hook

* docs: update PROJECT.md with session highlights

* chore: delete projectmd

* chore: update package.json and lock file

* chore: update package.json

* chore: remove patch

* chore: upgrade react types to 19 and show ai features only in development mode for now

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-24 11:38:48 +01:00

625 lines
13 KiB
CSS

@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'Inter';
src: url('/fonts/InterVariable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('/fonts/InterVariable-Italic.woff2') format('woff2');
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
:root {
border-radius: 10px;
scrollbar-gutter: auto !important;
overscroll-behavior: none;
background-color: transparent;
--safe-area-inset-top: env(safe-area-inset-top);
--safe-area-inset-right: env(safe-area-inset-right);
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left);
}
/* Fixes the scrollbar gutter issue for DaisyUI */
/* https://github.com/saadeghi/daisyui/issues/3040#issuecomment-2250530354 */
:root:has(:is(.modal-open, .modal:target, .modal-toggle:checked + .modal, .modal[open])) {
scrollbar-gutter: unset !important;
}
html[data-page='default'] {
background: theme('colors.base-100');
}
html[data-page='library'] {
background: theme('colors.base-200');
}
html[data-page='reader'] {
background: theme('colors.base-100');
}
body {
border-radius: 10px;
background-color: transparent;
cursor: default;
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
.full-height {
height: -webkit-fill-available;
}
@supports (height: 100dvh) {
.full-height {
height: 100dvh;
}
}
:root.edge-to-edge .full-height {
height: 100vh !important;
}
foliate-view {
display: block;
width: 100%;
height: 100%;
border: none;
}
foliate-fxl {
overflow: scroll;
}
@keyframes scaleUpAndDown {
0% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
100% {
transform: scale(1);
}
}
.hover-bar-anim:hover ~ .foliate-viewer {
animation: scaleUpAndDown 0.3s ease forwards;
}
.window-button {
@apply inline-flex h-6 w-6 items-center justify-center rounded-full;
@apply transform transition duration-200 ease-in-out hover:scale-105;
}
.rounded-window {
border-radius: 10px;
}
.rounded-window-top-left {
border-top-left-radius: 10px;
}
.rounded-window-bottom-left {
border-bottom-left-radius: 10px;
}
.rounded-window-top-right {
border-top-right-radius: 10px;
}
.rounded-window-bottom-right {
border-bottom-right-radius: 10px;
}
.window-border {
border-radius: 10px;
z-index: 99;
position: relative;
}
.window-border::before {
content: '';
z-index: 999;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid theme('colors.base-300');
border-radius: 10px;
pointer-events: none;
}
.dropdown-content {
position: relative;
@apply bg-base-100;
padding: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
border-radius: 14px;
}
.dropdown-content::before,
.dropdown-content::after {
content: '';
position: absolute;
top: -12px;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
}
.dropdown-content::before {
border-bottom: 12px solid theme('colors.base-100');
z-index: 1;
}
.dropdown-content::after {
top: -14px;
border-bottom: 13px solid rgba(0, 0, 0, 0.02);
z-index: 0;
}
.dropdown-content.bgcolor-base-200 {
background-color: theme('colors.base-200');
}
.dropdown-content.bgcolor-base-200::before {
border-bottom: 12px solid theme('colors.base-200');
z-index: 1;
}
.dropdown-content.bordercolor-content::before {
border-bottom: 12px solid theme('colors.base-content');
z-index: 1;
}
.dropdown-content.bordercolor-content::after {
top: -11px;
border-bottom: 13px solid theme('colors.base-100');
z-index: 1;
}
.dropdown-left::before,
.dropdown-left::after {
left: 20px;
transform: translateX(0);
}
.dropdown-center::before,
.dropdown-center::after {
left: 50%;
transform: translateX(-50%);
}
.dropdown-right::before,
.dropdown-right::after {
right: 20px;
transform: translateX(0);
}
.ui-rtl .dropdown-left::before,
.ui-rtl .dropdown-left::after {
left: auto;
right: 20px;
}
.ui-rtl .dropdown-right::before,
.ui-rtl .dropdown-right::after {
right: auto;
left: 20px;
}
.ui-rtl .dropdown-end > :where(.dropdown-content) {
inset-inline-end: auto;
inset-inline-start: 0;
}
.ui-rtl .settings-menu,
.ui-rtl .view-menu {
inset-inline-end: auto;
inset-inline-start: 0;
}
.ui-rtl .dropdown-content,
.ui-rtl .settings-menu,
.ui-rtl .view-menu {
direction: rtl;
}
.dropdown-content.no-triangle::before,
.dropdown-content.no-triangle::after {
display: none;
}
.tooltip.no-triangle::before,
.tooltip.no-triangle::after,
.tooltip-top.no-triangle::before,
.tooltip-top.no-triangle::after,
.tooltip-bottom.no-triangle::before,
.tooltip-bottom.no-triangle::after {
display: none;
}
.dropdown-content,
.settings-content {
font-size: 14px;
font-weight: 400;
}
@media (max-width: 768px) {
.dropdown-content,
.settings-content {
font-size: 16px;
}
}
.config-item {
@apply flex h-14 items-center justify-between p-4;
@apply hover:bg-base-100/50;
}
.config-item > span {
@apply line-clamp-2;
}
.collapse-arrow > .collapse-title:after {
top: var(--top-override, default-value);
inset-inline-end: var(--end-override, default-value);
}
.search-bar {
max-height: 0;
visibility: hidden;
overflow: visible;
transition: max-height 0.3s ease-out;
}
.search-bar-visible {
max-height: 100%;
visibility: visible;
}
.pull-indicator {
position: fixed;
width: 100%;
background-color: transparent;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-lighter);
transition: transform 0.2s ease-in-out;
z-index: 10;
}
.pull-indicator svg {
transition: transform 0.2s ease-in-out;
}
.pull-indicator.flip svg {
transform: rotate(180deg);
}
.content {
font-size: 16px;
}
@media (max-width: 480px) {
.content {
font-size: 20px;
}
}
@media (min-width: 481px) and (max-width: 1024px) {
.content {
font-size: 18.4px;
}
}
.content.font-size-base {
font-size: 1em !important;
line-height: 1.5em;
}
.content.font-size-sm {
font-size: 0.875em !important;
line-height: 1.25em;
}
.content.font-size-xs {
font-size: 0.75em !important;
line-height: 1em;
}
.writing-vertical-rl {
writing-mode: vertical-rl;
}
.direction-rtl {
direction: rtl;
}
.drop-zone {
position: relative;
}
.drag-over {
border: 2px dashed #4a90e2 !important;
background-color: rgba(74, 144, 226, 0.1) !important;
transition: all 0.2s ease-in-out;
}
.drag-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 20;
pointer-events: none;
}
.drop-zone.drag-over .drag-overlay {
display: block;
}
.drop-indicator {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 50;
padding: 20px;
background-color: theme('colors.base-300');
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
pointer-events: none;
}
.drop-zone.drag-over .drop-indicator {
display: block;
}
.touch-target {
position: relative;
}
.touch-target::before {
content: '';
position: absolute;
inset: -12px;
}
.no-context-menu {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
}
.book-spine {
background-image:
linear-gradient(
180deg,
hsla(0, 0%, 95%, 0.2) 0%,
/* Top highlight */ hsla(0, 0%, 90%, 0.1) 1%,
/* Top highlight */ transparent 2%,
/* Main book surface */ transparent 50%,
/* Main book surface */ hsla(0, 0%, 25%, 0.15) 95%,
/* Bottom shadow */ hsla(0, 0%, 15%, 0.2) 100% /* Bottom edge dark */
),
linear-gradient(
90deg,
hsla(0, 0%, 0%, 0.1) 0%,
/* Left binding shadow */ hsla(0, 0%, 20%, 0.2) 0.5%,
/* Dark transition */ hsla(0, 0%, 95%, 0.4) 0.8%,
/* Peak highlight */ hsla(0, 0%, 85%, 0.4) 2%,
/* Peak highlight */ hsla(0, 0%, 60%, 0.3) 2.8%,
/* Gradient highlight */ hsla(0, 0%, 40%, 0.2) 4.2%,
/* Shadow between highlights */ hsla(0, 0%, 70%, 0.15) 4.6%,
/* Second peak highlight */ hsla(0, 0%, 60%, 0.15) 5.4%,
/* Second gradient highlight */ hsla(0, 0%, 40%, 0.1) 6%,
/* Transition shadow */ transparent 88%,
/* Main book surface */ hsla(0, 0%, 30%, 0.1) 99%,
/* Right edge highlight */ hsla(0, 0%, 30%, 0.2) 99.5%,
/* Right edge shadow */ hsla(0, 0%, 10%, 0.2) 100% /* Right binding edge */
);
mask: linear-gradient(180deg, black 0%, rgba(0, 0, 0, 0.9) 50%, rgba(0, 0, 0, 0.5) 100%);
}
.visible-focus-inset-2:focus-visible {
outline: none;
box-shadow:
inset 0 0 0 2px transparent,
inset 0 0 0 3px #6396e7;
}
.window-button {
position: relative;
}
.window-button::before {
content: '';
position: absolute;
inset: -8px;
}
@keyframes spin-counterclockwise {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
.animate-reverse-spin {
animation: spin-counterclockwise 1s linear;
}
[data-eink='true'] [class*='shadow-'] {
box-shadow: none !important;
}
[data-eink='true'] [class*='text-base-content'] {
color: theme('colors.base-content') !important;
}
[data-eink='true'] [class*='text-blue'] {
color: theme('colors.base-content') !important;
font-weight: normal;
}
[data-eink='true'] [class*='text-red'] {
color: theme('colors.base-content') !important;
font-weight: normal;
}
[data-eink='true'] [class*='text-neutral-content'] {
color: theme('colors.base-content') !important;
}
[data-eink='true'] [class*='placeholder']::placeholder {
color: theme('colors.base-content') !important;
}
[data-eink='true'] [class*='bg-base-content'] {
background-color: theme('colors.base-content') !important;
}
[data-eink='true'] [class*='font-light'] {
font-weight: normal !important;
}
[data-eink='true'] [class*='text-bold-in-eink'] {
font-weight: bold;
}
[data-eink='true'] .eink-bordered {
border-width: 1px !important;
border-color: theme('colors.base-content') !important;
background-color: theme('colors.base-100') !important;
}
[data-eink='true'] input.search-input,
[data-eink='true'] input.search-input:focus {
border-width: 1px !important;
border-radius: 9999px;
border-color: theme('colors.base-content') !important;
background-color: theme('colors.base-100') !important;
}
[data-eink='true'] .modal-box {
box-shadow: none !important;
border-width: 1px !important;
border-color: theme('colors.base-content') !important;
}
[data-eink='true'] .dialog-overlay {
background-color: transparent !important;
}
[data-eink='true'] .menu-container {
background-color: theme('colors.base-100') !important;
border: 1px solid theme('colors.base-content') !important;
}
[data-eink='true'] hr {
border-color: theme('colors.base-content/0.75') !important;
}
[data-eink='true'] button,
[data-eink='true'] .btn {
color: theme('colors.base-content') !important;
}
[data-eink='true'] .btn-primary {
background-color: theme('colors.base-content') !important;
border: 1px solid theme('colors.base-content') !important;
color: theme('colors.base-100') !important;
}
[data-eink='true'] .bg-primary {
background-color: theme('colors.base-100') !important;
border: 1px solid theme('colors.base-content') !important;
color: theme('colors.base-content') !important;
}
[data-eink='true'] .dropdown-toggle {
background-color: theme('colors.transparent') !important;
}
[data-eink='true'] .opds-navigation .card {
background-color: theme('colors.base-100') !important;
border: 1px solid theme('colors.base-content') !important;
}
[data-eink='true'] .booknote-item {
background-color: theme('colors.base-100') !important;
border: 1px solid theme('colors.base-content') !important;
}
[data-eink='true'] .booknote-text {
background-color: theme('colors.base-100') !important;
}
[data-eink='true'] .bookitem-main,
[data-eink='true'] .groupitem-main {
border: 1px solid theme('colors.base-content') !important;
}
[data-eink='true'] .popup-container {
border: 1px solid theme('colors.base-content') !important;
background-color: theme('colors.base-100') !important;
}
[data-eink='true'] .popup-triangle-outer {
color: theme('colors.base-content') !important;
}
[data-eink='true'] .popup-triangle-inner {
color: theme('colors.base-100') !important;
}
[data-eink='true'] .annotation-notes {
transform-origin: top left;
transform: translate(-1px, -1px);
}
[data-eink='true'] .alert {
color: theme('colors.base-content') !important;
border: 1px solid theme('colors.base-content') !important;
background-color: theme('colors.base-100') !important;
}
[data-eink='true'] input[type='checkbox'] {
--tglbg: theme('colors.base-100') !important;
}
.no-transitions * {
transition: none !important;
}