agent-zero/webui/index.js
frdel d0369db0ed Squashed commit of the following:
commit f7b3e2540c
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Dec 8 19:42:17 2024 +0100

    knowledge import/reload

commit 4e028a3ce4
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Dec 8 11:28:47 2024 +0100

    Memory recall speedup

commit 8ec3b24696
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Dec 8 00:17:02 2024 +0100

    keyboard input tool

commit a76a302f3f
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sat Dec 7 23:28:03 2024 +0100

    solutions cleanup

commit 884007cdb0
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sat Dec 7 21:51:14 2024 +0100

    console print edits for docker

commit 927c234d69
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sat Dec 7 20:40:28 2024 +0100

    openai azure model func name fix

commit 53a46288f9
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 15:17:58 2024 +0100

    mistral fix, error text output

commit 6aa37744fc
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 14:58:10 2024 +0100

    toast fix

commit f0be03ea77
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 14:33:28 2024 +0100

    toast errors

commit 8434682812
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 11:30:34 2024 +0100

    warnings cleanup

commit 2b94af895d
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 10:54:06 2024 +0100

    Preload fix

commit 7f270d4a14
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 09:44:13 2024 +0100

    Server startup log msg

commit f9c9b5c933
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 07:50:15 2024 +0100

    Update run_ui.py

commit f3ca7e0742
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Dec 6 06:21:14 2024 +0100

    Update run_ui.py

commit 21975c5a7c
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Dec 5 20:45:51 2024 +0100

    local models docker url

commit f0a8b07c4f
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Dec 5 16:40:49 2024 +0100

    Server addr notice

commit 656612726a
Merge: 49594fe 7c2866c
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Thu Dec 5 16:11:23 2024 +0100

    Merge pull request #260 from 3clyp50/development

    fix: toast handling, mobile breakpoint

commit 7c2866ca61
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Dec 4 19:37:50 2024 +0100

    fix: toast handling, mobile breakpoint

    `toast.css` and `index.js`
    - fixed toasts disappearing right after showing
    - simplified toast animation

    `index.css`
    - set 2ⁿᵈ mobile breakpoint at 640px

commit 49594fe6ec
Merge: f697754 70b1fa3
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Wed Dec 4 10:39:58 2024 +0100

    Merge pull request #259 from 3clyp50/development

    CSS refactor and toasts

commit 70b1fa385a
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Dec 4 02:17:50 2024 +0100

    refactor: css, style: toasts, fix: z-index

    - organized structure
    - consolidated selectors and states
    - shorthand everywhere

    - modern toasts
    - bigger action buttons for mobile

commit f6977546c1
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 22:42:36 2024 +0100

    call subordinate fix

commit fbe47ac03e
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 21:19:03 2024 +0100

    Minor fixes

commit 961dbc405a
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 21:10:45 2024 +0100

    restart

commit 357909c16a
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 19:41:29 2024 +0100

    whisper remote preload

commit e0b0b6f636
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 17:39:56 2024 +0100

    nudge

commit 9fae02b2a5
Merge: 0ebc142 fedf2d4
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 14:57:18 2024 +0100

    Merge pull request #256 from 3clyp50/development

    feature: copy text button, nudge & fix: various styles

commit 0ebc142124
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 14:56:33 2024 +0100

    ssh connection retry

commit deae13d383
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 14:38:57 2024 +0100

    root pass fix

commit 9109fcbf60
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 14:28:53 2024 +0100

    root password change fix

commit 46689d6477
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Dec 3 14:22:18 2024 +0100

    RFC & SSH exchange for development

commit fedf2d4bdc
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Tue Dec 3 04:03:14 2024 +0100

    feature: copy text button, nudge & fix: various styles

    - Copy button for all messages
    - Nudge button front-end
    - Fixed various non-styled light mode elements

    to do -> css cleanup and whisper loading

commit 19f50d6d95
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Dec 1 20:50:17 2024 +0100

    attachments, files, prompt extras, prompt caching, refactors, cleanups

commit c99b1a47d4
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Nov 29 08:55:27 2024 +0100

    Alpine fix version, STT fixes

commit 81e653ba2d
Merge: 857f8b6 89b8483
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 28 23:08:09 2024 +0100

    Merge pull request #255 from 3clyp50/development

    feature: speech to text settings

commit 857f8b6d82
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 28 23:05:17 2024 +0100

    download and remove folders in browser

commit 89b848312b
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 28 16:07:50 2024 +0100

    feature: speech to text settings

    - initial commit: voice settings

    - Settings section for STT

commit b3a27bb442
Merge: 5e8d6b1 bb980ea
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 28 08:39:01 2024 +0100

    Merge pull request #254 from 3clyp50/development

    fix: file browser bugs + final ui polishing

commit bb980ea6b9
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 28 01:13:56 2024 +0100

    fix: file browser deletion bug + parent directory

    Underscore matters!
    - fixed both bugs for the browser

    Extra:
    - style for toasts

    quickfix generic modals

commit f0126a6ef8
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Nov 27 23:44:20 2024 +0100

    style: polishing and consistency

commit 5e8d6b1c7d
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Wed Nov 27 22:16:13 2024 +0100

    Minor fixes

commit 184f8dcf53
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Wed Nov 27 22:05:23 2024 +0100

    Pause button fix

commit 969f142af1
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Wed Nov 27 22:01:06 2024 +0100

    RFC fix, history bugfixes

commit 733b8de516
Merge: f2057d3 6a83e79
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Wed Nov 27 20:57:15 2024 +0100

    Merge branch 'pr/253' into development

commit 6a83e79d5a
Author: Alessandro <real.eclypso@gmail.com>
Date:   Wed Nov 27 20:41:53 2024 +0100

    fix: bigger modals

commit f2057d3901
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Wed Nov 27 17:30:19 2024 +0100

    Squashed commit of the following:

    commit e626817332
    Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
    Date:   Wed Nov 27 12:51:22 2024 +0100

        refactor: modals css

        Modals now get the base styles from modals.css, with any spec in the individual files (settings.css, file_manager.css, ecc).

    commit 306db0ca39
    Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
    Date:   Wed Nov 27 03:17:20 2024 +0100

        style: new action buttons + ghost buttons

        Updated styles for buttons, switches, and overall UI graphic improvement

    commit c95a379bb5
    Author: Alessandro <real.eclypso@gmail.com>
    Date:   Tue Nov 26 20:17:18 2024 +0100

        fix: status-icon

    commit eddafa8798
    Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
    Date:   Fri Nov 22 01:28:04 2024 +0100

        cleanup: webui folder cleanup (history)

        cleanup: webui sidebar, icons, modals

commit e626817332
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Nov 27 12:51:22 2024 +0100

    refactor: modals css

    Modals now get the base styles from modals.css, with any spec in the individual files (settings.css, file_manager.css, ecc).

commit 306db0ca39
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Nov 27 03:17:20 2024 +0100

    style: new action buttons + ghost buttons

    Updated styles for buttons, switches, and overall UI graphic improvement

commit c95a379bb5
Author: Alessandro <real.eclypso@gmail.com>
Date:   Tue Nov 26 20:17:18 2024 +0100

    fix: status-icon

commit eddafa8798
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Fri Nov 22 01:28:04 2024 +0100

    cleanup: webui folder cleanup (history)

    cleanup: webui sidebar, icons, modals

commit 22ecfd660c
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 25 22:14:17 2024 +0100

    intervention message fix

commit ea9c8bf63b
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 25 20:48:52 2024 +0100

    minor context window fixes

commit 489ca317c5
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 25 19:01:01 2024 +0100

    settings auth fix

commit a0ff118ad1
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 25 17:39:42 2024 +0100

    Context window management, work in progress

commit c0947e30c7
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 21 18:47:40 2024 +0100

    API separation

commit 8db8d3fa18
Merge: 5034892 0735bb9
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 21 15:29:20 2024 +0100

    Merge pull request #249 from 3clyp50/development

    feature: work_dir file manager

commit 0735bb9ae8
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 21 11:28:56 2024 +0100

    fix: SVG optimization

    Thanks SVGO!

    removal: settings.svg (not used)

commit 9c968ba1cf
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 21 10:58:20 2024 +0100

    feature: work_dir file manager

    Implemented the file browser for work_dir, we need to:
    - move endpoints away from run_ui.py
    - make the "Up" (parent dir) button work

    Extra:
    - Now when under 768px in width, you can touch outside of the sidebar to collapse it.

commit 50348926df
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 21:56:50 2024 +0100

    version info fix

commit 040de30ef2
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 21:21:47 2024 +0100

    removed bundles, tests

commit 020c16ef86
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 20:46:08 2024 +0100

    git+docker improvements

    version, build branch

commit 06260ed4a6
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 13:11:54 2024 +0100

    searxng fix, ui animation

commit 41dc7ae146
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 11:05:33 2024 +0100

    Nodejs eval require path fix

commit 970db9adc9
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 10:37:49 2024 +0100

    Whisper fix

commit f59ac2b485
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 09:34:00 2024 +0100

    docker /a0 mount fix

commit c7046fa97b
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 00:43:15 2024 +0100

    docker volume map fix

commit 0ce8344f0b
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 19 00:23:08 2024 +0100

    dockerfile, compose, smart cache

commit bad3951646
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 18 21:41:39 2024 +0100

    RFC error messages

commit 05cbaa0f4d
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 18 21:16:50 2024 +0100

    RFC password

    RFC password protection work in progress

commit 9d1d2be897
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 18 16:18:31 2024 +0100

    Dockerfile updates

commit a7a40ac18f
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 18 10:50:24 2024 +0100

    dotenv fix, knowledge tool fic

commit ba3422d452
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 18 09:01:23 2024 +0100

    dotenv fix, gitignore update

commit 9c7339042f
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 17 23:12:19 2024 +0100

    Squashed commit of the following:

    commit b05d44bb4bc9e07cfc0b584ab39e8624bae771fb
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Sun Nov 17 23:12:00 2024 +0100

        searxng, RFC, docker runtime

    commit c90fd4026e644d22e6c7dc29639c85eee6026828
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Sat Nov 16 21:21:49 2024 +0100

        Remote function calling

    commit f71d45ec7dbff4e2d3209f0efe97804f6e602fe7
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Fri Nov 15 13:13:09 2024 +0100

        Fix for bool arg parsing

    commit 936768d1d8efc9060494334b87f400c933d78048
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Fri Nov 15 13:01:28 2024 +0100

        Dynamic runtime args parsing

    commit 00c915fc6c1f8f00f8176fbf5b77af32fa312d18
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Fri Nov 15 12:13:58 2024 +0100

        API key fix

    commit 504a7f91789caa16578af8bae9b7936a9d7fbbb7
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Fri Nov 15 11:59:41 2024 +0100

        API keys JIT loading

    commit 5678a2fce2d333454bb1a2e94ca2b5916d321b41
    Author: frdel <38891707+frdel@users.noreply.github.com>
    Date:   Fri Nov 15 11:27:12 2024 +0100

        Update dotenv.py

commit e469f6d7ba
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Nov 15 09:57:49 2024 +0100

    Docker runtime preload

commit 66f1ab7baf
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Fri Nov 15 09:49:23 2024 +0100

    Docker runtime - SSH, runtime args

commit 02cb41b2fd
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 14 21:49:45 2024 +0100

    WIP: docker runtime

commit b33b48057d
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 14 21:28:14 2024 +0100

    WIP: docker runtime

commit 7fc17b39c5
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 14 20:27:43 2024 +0100

    Docker runtime in progress

    work in progress
    container manager script
    runtime image with autostart

commit 2bf24b76d9
Merge: 92d94b4 a57f0c1
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 12 15:38:30 2024 +0100

    Merge pull request #239 from 3clyp50/development

    feature: attachments preview and sending (file, code, imgs)

commit a57f0c1198
Author: Alessandro <real.eclypso@gmail.com>
Date:   Tue Nov 12 15:02:25 2024 +0100

    feature: attachments preview and sending (file, code, imgs)

commit 92d94b4d86
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 10 23:44:15 2024 +0100

    TTS prototype

    TTS with default browser API

commit 1a91334a42
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 10 20:57:49 2024 +0100

    STT continued

    Dialogue mode and state managed for STT

commit 22f1a2b744
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 10 14:25:20 2024 +0100

    speech recognition prototype

    using xenova web only tts

commit a0b042cfb2
Merge: 22db39f 82ca0d8
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 10 08:52:32 2024 +0100

    Merge pull request #235 from 3clyp50/development

    feature: openai-whisper voice input

commit 22db39f731
Merge: d39beba 2b1aa09
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Sun Nov 10 08:52:15 2024 +0100

    Merge pull request #236 from linuztx/development

    Add Free Cloudflare Tunnel Support for Remote Access

commit 2b1aa09840
Author: linuztx <linuztx@gmail.com>
Date:   Sun Nov 10 14:56:52 2024 +0800

    Add auto-downloading cloudflared tunnel manager

commit cca85d7f5d
Author: linuztx <linuztx@gmail.com>
Date:   Sun Nov 10 14:54:52 2024 +0800

    Integrate Cloudflare tunnel support in web UI

commit 433f44522c
Author: linuztx <linuztx@gmail.com>
Date:   Sun Nov 10 14:54:19 2024 +0800

    Add USE_CLOUDFLARE environment variable

commit d94c3b0467
Author: linuztx <linuztx@gmail.com>
Date:   Sun Nov 10 14:53:45 2024 +0800

    Cloudflared binaries

commit 451fdb08c4
Author: linuztx <linuztx@gmail.com>
Date:   Sun Nov 10 14:52:24 2024 +0800

    Add bin directory for cloudflared downloads

commit 82ca0d800a
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Sun Nov 10 00:37:41 2024 +0100

    feature: openai-whisper voice input

    This also reverts commit 92a904d4411a203c482bc1231dee1438d7279b62.

commit 3c6a5bee64
Author: Alessandro <real.eclypso@gmail.com>
Date:   Fri Nov 8 01:46:29 2024 +0100

    feature: attachment setup

    missing
    - double user message when sending imgs
    - base 64 images implementation

    fix: fonts consistency

commit d39beba374
Merge: 2eb497c 9d6b769
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Thu Nov 7 08:31:52 2024 +0100

    Merge pull request #234 from 3clyp50/development

    UI Knowledge import, attachments, voice input and scroll fix

commit 9d6b769dc2
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 7 05:40:40 2024 +0100

    UI Knowledge import

    missing
    - image attachment
    - work_dir browser (backend implemented, WIP)
    - WHISPER (hurry up)

commit 3c40c86d07
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Thu Nov 7 03:40:06 2024 +0100

    Revert index.js scrolling logic + css infinite scroll fix

commit d84469dff6
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Nov 6 11:17:56 2024 +0100

    Mic js and embedding menu + styles

commit 2eb497c8d2
Merge: ef1cdac 21933bc
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 5 21:33:32 2024 +0100

    Merge pull request #233 from 3clyp50/development

    Animation, KaTeX fix and mobile improvements

commit 21933bce2f
Author: Alessandro <real.eclypso@gmail.com>
Date:   Tue Nov 5 21:07:50 2024 +0100

    KaTeX fix and mobile improvements

commit ef1cdacea2
Merge: 9626c04 553f7bf
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Tue Nov 5 17:54:14 2024 +0100

    Merge pull request #232 from 3clyp50/development

    LaTeX, old browser support, new buttons and attachments

commit 553f7bf039
Merge: fc03a79 9626c04
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Tue Nov 5 14:48:53 2024 +0100

    Merge remote-tracking branch 'upstream/development' into development

commit fc03a7922e
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Tue Nov 5 00:59:25 2024 +0100

    Browsers support, new text buttons + attachments

    - Firefox/old browsers support and new text buttons + attachments
    - LaTeX support!

commit 9626c044d5
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 4 22:55:56 2024 +0100

    UI and settings merge

commit 255baf0780
Merge: 1c026ee 61b5b83
Author: Jan Tomášek <38891707+frdel@users.noreply.github.com>
Date:   Mon Nov 4 22:26:57 2024 +0100

    Merge pull request #229 from 3clyp50/development

    UI update

commit 61b5b8389a
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Mon Nov 4 22:26:07 2024 +0100

    other things + Embedding Model selection

commit 6ff3df03de
Author: Alessandro <real.eclypso@gmail.com>
Date:   Mon Nov 4 21:06:48 2024 +0100

    toast!

commit e6ac772a2e
Author: Alessandro <real.eclypso@gmail.com>
Date:   Mon Nov 4 20:58:55 2024 +0100

    Mobile and UX update

commit 1a0ceebcaf
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Mon Nov 4 15:18:06 2024 +0100

    Modal styling WIP

commit f28a05d739
Author: Alessandro <155005371+3clyp50@users.noreply.github.com>
Date:   Wed Oct 23 00:18:59 2024 +0200

    Improved UI/UX in WebUI

    - Collapsible pref section 👍
    - Monospace font
    - UX focus on user feedback and accessibility
    - Mobile and input section QoL
    - Other minor refinements

commit 1c026ee75f
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Tue Oct 29 19:39:54 2024 +0100

    Behaviour prompt

    Prototype of adjustable behaviour system prompt

commit a5d671904d
Author: frdel <38891707+frdel@users.noreply.github.com>
Date:   Sun Oct 27 18:04:40 2024 +0100

    Settings prototype

    Settings modal window managed from python - work in progress
2024-12-08 21:34:56 +01:00

872 lines
No EOL
26 KiB
JavaScript

import * as msgs from "./js/messages.js";
import { speech } from "./js/speech.js";
const leftPanel = document.getElementById('left-panel');
const rightPanel = document.getElementById('right-panel');
const container = document.querySelector('.container');
const chatInput = document.getElementById('chat-input');
const chatHistory = document.getElementById('chat-history');
const sendButton = document.getElementById('send-button');
const inputSection = document.getElementById('input-section');
const statusSection = document.getElementById('status-section');
const chatsSection = document.getElementById('chats-section');
const progressBar = document.getElementById('progress-bar');
const autoScrollSwitch = document.getElementById('auto-scroll-switch');
const timeDate = document.getElementById('time-date-container');
let autoScroll = true;
let context = "";
let connectionStatus = false
// Initialize the toggle button
setupSidebarToggle();
function isMobile() {
return window.innerWidth <= 768;
}
function toggleSidebar(show) {
const overlay = document.getElementById('sidebar-overlay');
if (typeof show === 'boolean') {
leftPanel.classList.toggle('hidden', !show);
rightPanel.classList.toggle('expanded', !show);
overlay.classList.toggle('visible', show);
} else {
leftPanel.classList.toggle('hidden');
rightPanel.classList.toggle('expanded');
overlay.classList.toggle('visible', !leftPanel.classList.contains('hidden'));
}
}
function handleResize() {
const overlay = document.getElementById('sidebar-overlay');
if (isMobile()) {
leftPanel.classList.add('hidden');
rightPanel.classList.add('expanded');
overlay.classList.remove('visible');
} else {
leftPanel.classList.remove('hidden');
rightPanel.classList.remove('expanded');
overlay.classList.remove('visible');
}
}
window.addEventListener('load', handleResize);
window.addEventListener('resize', handleResize);
document.addEventListener('DOMContentLoaded', () => {
const overlay = document.getElementById('sidebar-overlay');
overlay.addEventListener('click', () => {
if (isMobile()) {
toggleSidebar(false);
}
});
});
function setupSidebarToggle() {
const leftPanel = document.getElementById('left-panel');
const rightPanel = document.getElementById('right-panel');
const toggleSidebarButton = document.getElementById('toggle-sidebar');
if (toggleSidebarButton) {
toggleSidebarButton.addEventListener('click', toggleSidebar);
} else {
console.error('Toggle sidebar button not found');
setTimeout(setupSidebarToggle, 100);
}
}
document.addEventListener('DOMContentLoaded', setupSidebarToggle);
export async function sendMessage() {
try {
const message = chatInput.value.trim();
const inputAD = Alpine.$data(inputSection);
const attachments = inputAD.attachments;
const hasAttachments = attachments && attachments.length > 0;
if (message || hasAttachments) {
let response;
const messageId = generateGUID();
// Include attachments in the user message
if (hasAttachments) {
const attachmentsWithUrls = attachments.map(attachment => {
if (attachment.type === 'image') {
return {
...attachment,
url: URL.createObjectURL(attachment.file)
};
} else {
return {
...attachment
};
}
});
// Render user message with attachments
setMessage(messageId, 'user', '', message, false, {
attachments: attachmentsWithUrls
});
const formData = new FormData();
formData.append('text', message);
formData.append('context', context);
formData.append('message_id', messageId);
for (let i = 0; i < attachments.length; i++) {
formData.append('attachments', attachments[i].file);
}
response = await fetch('/message_async', {
method: 'POST',
body: formData
});
} else {
// For text-only messages
const data = {
text: message,
context,
message_id: messageId
};
response = await fetch('/message_async', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
}
// Handle response
const jsonResponse = await response.json();
if (!jsonResponse) {
toast("No response returned.", "error");
}
// else if (!jsonResponse.ok) {
// if (jsonResponse.message) {
// toast(jsonResponse.message, "error");
// } else {
// toast("Undefined error.", "error");
// }
// }
else {
setContext(jsonResponse.context);
}
// Clear input and attachments
chatInput.value = '';
inputAD.attachments = [];
inputAD.hasAttachments = false;
adjustTextareaHeight();
}
} catch (e) {
toastFetchError("Error sending message", e)
}
}
function toastFetchError(text, error) {
if (getConnectionStatus()) {
toast(`${text}: ${error.message}`, "error");
} else {
toast(`${text} (it seems the backend is not running): ${error.message}`, "error");
}
console.error(text, error);
}
window.toastFetchError = toastFetchError
chatInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
sendButton.addEventListener('click', sendMessage);
export function updateChatInput(text) {
console.log('updateChatInput called with:', text);
// Append text with proper spacing
const currentValue = chatInput.value;
const needsSpace = currentValue.length > 0 && !currentValue.endsWith(' ');
chatInput.value = currentValue + (needsSpace ? ' ' : '') + text + ' ';
// Adjust height and trigger input event
adjustTextareaHeight();
chatInput.dispatchEvent(new Event('input'));
console.log('Updated chat input value:', chatInput.value);
}
function updateUserTime() {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
const ampm = hours >= 12 ? 'pm' : 'am';
const formattedHours = hours % 12 || 12;
// Format the time
const timeString = `${formattedHours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')} ${ampm}`;
// Format the date
const options = { year: 'numeric', month: 'short', day: 'numeric' };
const dateString = now.toLocaleDateString(undefined, options);
// Update the HTML
const userTimeElement = document.getElementById('time-date');
userTimeElement.innerHTML = `${timeString}<br><span id="user-date">${dateString}</span>`;
}
updateUserTime();
setInterval(updateUserTime, 1000);
function setMessage(id, type, heading, content, temp, kvps = null) {
// Search for the existing message container by id
let messageContainer = document.getElementById(`message-${id}`);
if (messageContainer) {
// Don't re-render user messages
if (type === 'user') {
return; // Skip re-rendering
}
// For other types, update the message
messageContainer.innerHTML = '';
} else {
// Create a new container if not found
const sender = type === 'user' ? 'user' : 'ai';
messageContainer = document.createElement('div');
messageContainer.id = `message-${id}`;
messageContainer.classList.add('message-container', `${sender}-container`);
if (temp) messageContainer.classList.add("message-temp");
}
const handler = msgs.getHandler(type);
handler(messageContainer, id, type, heading, content, temp, kvps);
// If the container was found, it was already in the DOM, no need to append again
if (!document.getElementById(`message-${id}`)) {
chatHistory.appendChild(messageContainer);
}
if (autoScroll) chatHistory.scrollTop = chatHistory.scrollHeight;
}
window.loadKnowledge = async function () {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.txt,.pdf,.csv,.html,.json,.md';
input.multiple = true;
input.onchange = async () => {
try{
const formData = new FormData();
for (let file of input.files) {
formData.append('files[]', file);
}
formData.append('ctxid', getContext());
const response = await fetch('/import_knowledge', {
method: 'POST',
body: formData,
});
if (!response.ok) {
toast(await response.text(), "error");
} else {
const data = await response.json();
toast("Knowledge files imported: " + data.filenames.join(", "), "success");
}
} catch (e) {
toastFetchError("Error loading knowledge", e)
}
};
input.click();
}
function adjustTextareaHeight() {
chatInput.style.height = 'auto';
chatInput.style.height = (chatInput.scrollHeight) + 'px';
}
export const sendJsonData = async function (url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.text();
throw new Error(error);
}
const jsonResponse = await response.json();
return jsonResponse;
}
window.sendJsonData = sendJsonData
function generateGUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0;
var v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function getConnectionStatus() {
return connectionStatus
}
function setConnectionStatus(connected) {
connectionStatus = connected
const statusIcon = Alpine.$data(timeDate.querySelector('.status-icon'));
statusIcon.connected = connected
}
let lastLogVersion = 0;
let lastLogGuid = ""
let lastSpokenNo = 0
async function poll() {
let updated = false
try {
const response = await sendJsonData("/poll", { log_from: lastLogVersion, context });
//console.log(response)
if (!context) setContext(response.context)
if (response.context != context) return //skip late polls after context change
if (lastLogGuid != response.log_guid) {
chatHistory.innerHTML = ""
lastLogVersion = 0
}
if (lastLogVersion != response.log_version) {
updated = true
for (const log of response.logs) {
const messageId = log.id || log.no; // Use log.id if available
setMessage(messageId, log.type, log.heading, log.content, log.temp, log.kvps);
}
afterMessagesUpdate(response.logs)
}
updateProgress(response.log_progress, response.log_progress_active)
//set ui model vars from backend
const inputAD = Alpine.$data(inputSection);
inputAD.paused = response.paused;
// Update status icon state
setConnectionStatus(true)
const chatsAD = Alpine.$data(chatsSection);
chatsAD.contexts = response.contexts;
lastLogVersion = response.log_version;
lastLogGuid = response.log_guid;
} catch (error) {
console.error('Error:', error);
setConnectionStatus(false)
}
return updated
}
function afterMessagesUpdate(logs) {
if (localStorage.getItem('speech') == 'true') {
speakMessages(logs)
}
}
function speakMessages(logs) {
// log.no, log.type, log.heading, log.content
for (let i = logs.length - 1; i >= 0; i--) {
const log = logs[i]
if (log.type == "response") {
if (log.no > lastSpokenNo) {
lastSpokenNo = log.no
speech.speak(log.content)
return
}
}
}
}
function updateProgress(progress, active) {
if (!progress) progress = ""
if (!active) {
removeClassFromElement(progressBar, "shiny-text")
} else {
addClassToElement(progressBar, "shiny-text")
}
if (progressBar.innerHTML != progress) {
progressBar.innerHTML = progress
}
}
window.pauseAgent = async function (paused) {
try {
const resp = await sendJsonData("/pause", { paused: paused, context });
} catch (e) {
window.toastFetchError("Error pausing agent", e)
}
}
window.resetChat = async function () {
try {
const resp = await sendJsonData("/chat_reset", { context });
updateAfterScroll()
} catch (e) {
window.toastFetchError("Error resetting chat", e)
}
}
window.newChat = async function () {
try {
setContext(generateGUID());
updateAfterScroll()
} catch (e) {
window.toastFetchError("Error creating new chat", e)
}
}
window.killChat = async function (id) {
try {
const chatsAD = Alpine.$data(chatsSection);
let found, other
for (let i = 0; i < chatsAD.contexts.length; i++) {
if (chatsAD.contexts[i].id == id) {
found = true
} else {
other = chatsAD.contexts[i]
}
if (found && other) break
}
if (context == id && found) {
if (other) setContext(other.id)
else setContext(generateGUID())
}
if (found) sendJsonData("/chat_remove", { context: id });
updateAfterScroll()
} catch (e) {
window.toastFetchError("Error creating new chat", e)
}
}
window.selectChat = async function (id) {
setContext(id)
updateAfterScroll()
}
export const setContext = function (id) {
if (id == context) return
context = id
lastLogGuid = ""
lastLogVersion = 0
lastSpokenNo = 0
const chatsAD = Alpine.$data(chatsSection);
chatsAD.selected = id
}
export const getContext = function () {
return context
}
window.toggleAutoScroll = async function (_autoScroll) {
autoScroll = _autoScroll;
}
window.toggleJson = async function (showJson) {
// add display:none to .msg-json class definition
toggleCssProperty('.msg-json', 'display', showJson ? 'block' : 'none');
}
window.toggleThoughts = async function (showThoughts) {
// add display:none to .msg-json class definition
toggleCssProperty('.msg-thoughts', 'display', showThoughts ? undefined : 'none');
}
window.toggleUtils = async function (showUtils) {
// add display:none to .msg-json class definition
toggleCssProperty('.message-util', 'display', showUtils ? undefined : 'none');
// toggleCssProperty('.message-util .msg-kvps', 'display', showUtils ? undefined : 'none');
// toggleCssProperty('.message-util .msg-content', 'display', showUtils ? undefined : 'none');
}
window.toggleDarkMode = function (isDark) {
if (isDark) {
document.body.classList.remove('light-mode');
} else {
document.body.classList.add('light-mode');
}
console.log("Dark mode:", isDark);
localStorage.setItem('darkMode', isDark);
};
window.toggleSpeech = function (isOn) {
console.log("Speech:", isOn);
localStorage.setItem('speech', isOn);
if (!isOn) speech.stop()
};
window.nudge = async function () {
try {
const resp = await sendJsonData("/nudge", { ctxid: getContext() });
} catch (e) {
toastFetchError("Error nudging agent", e)
}
}
window.restart = async function () {
try {
if (!getConnectionStatus()) {
toast("Backend disconnected, cannot restart.", "error");
return
}
// First try to initiate restart
const resp = await sendJsonData("/restart", {});
} catch (e) {
// Show restarting message
toast("Restarting...", "info", 0);
let retries = 0;
const maxRetries = 60; // Maximum number of retries (15 seconds with 250ms interval)
while (retries < maxRetries) {
try {
const resp = await sendJsonData("/health", {});
// Server is back up, show success message
await new Promise(resolve => setTimeout(resolve, 250));
hideToast();
await new Promise(resolve => setTimeout(resolve, 400));
toast("Restarted", "success", 5000);
return;
} catch (e) {
// Server still down, keep waiting
retries++;
await new Promise(resolve => setTimeout(resolve, 250));
}
}
// If we get here, restart failed or took too long
hideToast();
await new Promise(resolve => setTimeout(resolve, 400));
toast("Restart timed out or failed", "error", 5000);
}
}
// Modify this part
document.addEventListener('DOMContentLoaded', () => {
const isDarkMode = localStorage.getItem('darkMode') !== 'false';
toggleDarkMode(isDarkMode);
});
window.toggleDarkMode = function (isDark) {
if (isDark) {
document.body.classList.remove('light-mode');
} else {
document.body.classList.add('light-mode');
}
console.log("Dark mode:", isDark);
localStorage.setItem('darkMode', isDark);
};
function toggleCssProperty(selector, property, value) {
// Get the stylesheet that contains the class
const styleSheets = document.styleSheets;
// Iterate through all stylesheets to find the class
for (let i = 0; i < styleSheets.length; i++) {
const styleSheet = styleSheets[i];
const rules = styleSheet.cssRules || styleSheet.rules;
for (let j = 0; j < rules.length; j++) {
const rule = rules[j];
if (rule.selectorText == selector) {
// Check if the property is already applied
if (value === undefined) {
rule.style.removeProperty(property);
} else {
rule.style.setProperty(property, value);
}
return;
}
}
}
}
window.loadChats = async function () {
try {
const fileContents = await readJsonFiles();
const response = await sendJsonData("/chat_load", { chats: fileContents });
if (!response) {
toast("No response returned.", "error")
}
// else if (!response.ok) {
// if (response.message) {
// toast(response.message, "error")
// } else {
// toast("Undefined error.", "error")
// }
// }
else {
setContext(response.ctxids[0])
toast("Chats loaded.", "success")
}
} catch (e) {
toastFetchError("Error loading chats", e)
}
}
window.saveChat = async function () {
try {
const response = await sendJsonData("/chat_export", { ctxid: context });
if (!response) {
toast("No response returned.", "error")
}
// else if (!response.ok) {
// if (response.message) {
// toast(response.message, "error")
// } else {
// toast("Undefined error.", "error")
// }
// }
else {
downloadFile(response.ctxid + ".json", response.content)
toast("Chat file downloaded.", "success")
}
} catch (e) {
toastFetchError("Error saving chat", e)
}
}
function downloadFile(filename, content) {
// Create a Blob with the content to save
const blob = new Blob([content], { type: 'application/json' });
// Create a link element
const link = document.createElement('a');
// Create a URL for the Blob
const url = URL.createObjectURL(blob);
link.href = url;
// Set the file name for download
link.download = filename;
// Programmatically click the link to trigger the download
link.click();
// Clean up by revoking the object URL
setTimeout(() => {
URL.revokeObjectURL(url);
}, 0);
}
function readJsonFiles() {
return new Promise((resolve, reject) => {
// Create an input element of type 'file'
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json'; // Only accept JSON files
input.multiple = true; // Allow multiple file selection
// Trigger the file dialog
input.click();
// When files are selected
input.onchange = async () => {
const files = input.files;
if (!files.length) {
resolve([]); // Return an empty array if no files are selected
return;
}
// Read each file as a string and store in an array
const filePromises = Array.from(files).map(file => {
return new Promise((fileResolve, fileReject) => {
const reader = new FileReader();
reader.onload = () => fileResolve(reader.result);
reader.onerror = fileReject;
reader.readAsText(file);
});
});
try {
const fileContents = await Promise.all(filePromises);
resolve(fileContents);
} catch (error) {
reject(error); // In case of any file reading error
}
};
});
}
function addClassToElement(element, className) {
element.classList.add(className);
}
function removeClassFromElement(element, className) {
element.classList.remove(className);
}
function toast(text, type = 'info', timeout = 5000) {
const toast = document.getElementById('toast');
const isVisible = toast.classList.contains('show');
// Clear any existing timeout immediately
if (toast.timeoutId) {
clearTimeout(toast.timeoutId);
toast.timeoutId = null;
}
// Function to update toast content and show it
const updateAndShowToast = () => {
// Update the toast content and type
const title = type.charAt(0).toUpperCase() + type.slice(1);
toast.querySelector('.toast__title').textContent = title;
toast.querySelector('.toast__message').textContent = text;
// Remove old classes and add new ones
toast.classList.remove('toast--success', 'toast--error', 'toast--info');
toast.classList.add(`toast--${type}`);
// Show/hide copy button based on toast type
const copyButton = toast.querySelector('.toast__copy');
copyButton.style.display = type === 'error' ? 'inline-block' : 'none';
// Add the close button event listener
const closeButton = document.querySelector('.toast__close');
closeButton.onclick = () => {
hideToast();
};
// Add the copy button event listener
copyButton.onclick = () => {
navigator.clipboard.writeText(text);
copyButton.textContent = 'Copied!';
setTimeout(() => {
copyButton.textContent = 'Copy';
}, 2000);
};
// Show the toast
toast.style.display = 'flex';
// Force a reflow to ensure the animation triggers
void toast.offsetWidth;
toast.classList.add('show');
// Set timeout if specified
if (timeout) {
const minTimeout = Math.max(timeout, 5000);
toast.timeoutId = setTimeout(() => {
hideToast();
}, minTimeout);
}
};
if (isVisible) {
// If a toast is visible, hide it first then show the new one
toast.classList.remove('show');
toast.classList.add('hide');
// Wait for hide animation to complete before showing new toast
setTimeout(() => {
toast.classList.remove('hide');
updateAndShowToast();
}, 400); // Match this with CSS transition duration
} else {
// If no toast is visible, show the new one immediately
updateAndShowToast();
}
}
function hideToast() {
const toast = document.getElementById('toast');
// Clear any existing timeout
if (toast.timeoutId) {
clearTimeout(toast.timeoutId);
toast.timeoutId = null;
}
toast.classList.remove('show');
toast.classList.add('hide');
// Wait for the hide animation to complete before removing from display
setTimeout(() => {
toast.style.display = 'none';
toast.classList.remove('hide');
}, 400); // Match this with CSS transition duration
}
function scrollChanged(isAtBottom) {
const inputAS = Alpine.$data(autoScrollSwitch);
inputAS.autoScroll = isAtBottom
// autoScrollSwitch.checked = isAtBottom
}
function updateAfterScroll() {
// const toleranceEm = 1; // Tolerance in em units
// const tolerancePx = toleranceEm * parseFloat(getComputedStyle(document.documentElement).fontSize); // Convert em to pixels
const tolerancePx = 50;
const chatHistory = document.getElementById('chat-history');
const isAtBottom = (chatHistory.scrollHeight - chatHistory.scrollTop) <= (chatHistory.clientHeight + tolerancePx);
scrollChanged(isAtBottom);
}
chatHistory.addEventListener('scroll', updateAfterScroll);
chatInput.addEventListener('input', adjustTextareaHeight);
// setInterval(poll, 250);
async function startPolling() {
const shortInterval = 25
const longInterval = 250
const shortIntervalPeriod = 100
let shortIntervalCount = 0
async function _doPoll() {
let nextInterval = longInterval
try {
const result = await poll();
if (result) shortIntervalCount = shortIntervalPeriod; // Reset the counter when the result is true
if (shortIntervalCount > 0) shortIntervalCount--; // Decrease the counter on each call
nextInterval = shortIntervalCount > 0 ? shortInterval : longInterval;
} catch (error) {
console.error('Error:', error);
}
// Call the function again after the selected interval
setTimeout(_doPoll.bind(this), nextInterval);
}
_doPoll();
}
document.addEventListener("DOMContentLoaded", startPolling);