Merge branch 'staging' into tool-calling

689dbad2d16a0d50df2146b535b3c067277a35b1

Cohee <18619528+Cohee1207@users.noreply.github.com>

14 files changed, +1316 -12Showing whitespace changes
.github/readme-de_de.md+383 -0
@@ -0,0 +1,383 @@
1+> [!IMPORTANT]
2+> Die hier veröffentlichten Informationen sind möglicherweise veraltet oder unvollständig. Für aktuelle Informationen nutzen Sie bitte die englische Version.
3+> Letztes Update dieser README: 28.9.2024
4+
5+<a name="readme-top"></a>
6+
7+![][cover]
8+
9+<div align="center">
10+
11+[English](readme.md) | German | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
12+
13+[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
14+[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
15+[![GitHub Issues](https://img.shields.io/github/issues/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/issues)
16+[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/pulls)
17+
18+</div>
19+
20+---
21+
22+SillyTavern bietet eine einheitliche Benutzeroberfläche für viele LLM-APIs (KoboldAI/CPP, Horde, NovelAI, Ooba, Tabby, OpenAI, OpenRouter, Claude, Mistral und mehr), ein mobilfreundliches Layout, einen Visual-Novel-Modus, die Integration von Automatic1111 & ComfyUI API zur Bilderzeugung, TTS, WorldInfo (Lorebooks), anpassbare UI, automatische Übersetzung, mehr Eingabeaufforderungsoptionen, als du jemals wolltest oder brauchst, und unendliches Wachstumspotenzial durch Drittanbietererweiterungen.
23+
24+Wir haben eine [Dokumentationswebsite](https://docs.sillytavern.app/), um die meisten deiner Fragen zu beantworten und dir den Einstieg zu erleichtern.
25+
26+## Was ist SillyTavern?
27+
28+SillyTavern (oder ST abgekürtz) ist eine lokal installierte Benutzeroberfläche, die es dir ermöglicht, mit Textgenerations-LLMs, Bildgenerierungsmaschinen und TTS-Sprachmodellen zu interagieren.
29+
30+Angefangen im Februar 2023 als Fork von TavernAI 1.2.8 hat SillyTavern nun über 100 Mitwirkende und 2 Jahre unabhängiger Entwicklung hinter sich und dient weiterhin als führende Software für versierte KI-Hobbyisten.
31+
32+## Unsere Vision
33+
34+1. Wir möchten die Nutzer mit so viel Nutzen und Kontrolle über ihre LLM-Prompts wie möglich ausstatten. Die steile Lernkurve ist Teil des Spaßes!
35+2. Wir bieten weder Online- oder gehosteten Dienste an, noch verfolgen wir programmgesteuert Benutzerdaten.
36+3. SillyTavern ist ein Herzensprojekt, das von einer engagierten Community von LLM-Enthusiasten unterstützt wird, und wird immer kostenlos und Open Source sein.
37+
38+## Branches
39+
40+SillyTavern wird mit einem Branchsystem entwickelt, um ein reibungsloses Erlebnis für alle Nutzer zu gewährleisten.
41+
42+* `release` -🌟 **Empfohlen für die meisten Nutzer.** Dies ist der stabilste und empfohlene Branch, der nur aktualisiert wird, wenn wichtige Versionen veröffentlicht werden. Er ist für die Mehrheit der Nutzer geeignet. Typischerweise einmal im Monat aktualisiert.
43+* `staging` - ⚠️ **Nicht für den gelegentlichen Gebrauch empfohlen.** Dieser Branch enthält die neuesten Funktionen, kann jedoch jederzeit instabil sein. Nur für Power-User und Enthusiasten. Mehrmals täglich aktualisiert.
44+
45+Wenn du nicht vertraut mit der Verwendung der git CLI bist oder nicht verstehst, was ein Branch ist, mach dir keine Sorgen! Der Release-Branch ist immer die bevorzugte Option für dich.
46+
47+## Was brauche ich zusätzlich zu SillyTavern?
48+
49+Da SillyTavern nur eine Benutzeroberfläche ist, benötigst du Zugriff auf ein LLM-Backend, um Inferenz bereitzustellen. Du kannst AI Horde für sofortiges Chatten ohne weitere Einrichtung verwenden. Darüber hinaus unterstützen wir viele andere lokale und cloudbasierte LLM-Backends: OpenAI-kompatible API, KoboldAI, Tabby und viele mehr. Du kannst mehr über unsere unterstützten APIs in [der FAQ](https://docs.sillytavern.app/usage/api-connections/) lesen.
50+
51+### Brauche ich einen leistungsstarken PC, um SillyTavern auszuführen?
52+
53+Die Hardwareanforderungen sind minimal: Es läuft auf allem, was NodeJS 18 oder höher ausführen kann. Wenn du LLM-Inferenz auf deinem lokalen Rechner durchführen möchtest, empfehlen wir eine NVIDIA-Grafikkarte der 3000er-Serie mit mindestens 6 GB VRAM. Überprüfe die Dokumentation deines Backends für weitere Einzelheiten.
54+
55+### Vorgeschlagene Backends (keine Partnerschaft oder Werbebeziehung)
56+
57+* [AI Horde](https://aihorde.net/) - verwende Modelle, die von Freiwilligen gehostet werden. Erfordert keine weitere Einrichtung
58+* [KoboldCpp](https://github.com/LostRuins/koboldcpp) - ein Favorit der Community, um GGUF-Modelle lokal auszuführen
59+* [tabbyAPI](https://github.com/theroyallab/tabbyAPI) - eine beliebte, portable, speicherplatzoptimierte und lokal gehostete exl2 Inferenz-API
60+* [OpenRouter](https://openrouter.ai) - eine einzige API für viele Cloud-Anbieter (OpenAI, Claude, Meta Llama usw.) sowie beliebte Community-Modelle.
61+
62+## Fragen oder Vorschläge?
63+
64+### Discord-Server
65+
66+| [![][discord-shield-badge]][discord-link] | [Tritt unserer Discord-Community bei!](https://discord.gg/sillytavern) Erhalte Unterstützung, teile deine Lieblingscharaktere und Prompts. |
67+| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
68+
69+Oder nimm direkt Kontakt mit den Entwicklern auf:
70+
71+* Discord: cohee, rossascends, wolfsblvt
72+* Reddit: [/u/RossAscends](https://www.reddit.com/user/RossAscends/), [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/)
73+* [Erstelle ein GitHub-Issue](https://github.com/SillyTavern/SillyTavern/issues)
74+
75+### Ich mag dieses Projekt! Wie kann ich beitragen?
76+
77+1. Sende Pull-Requests. Lerne, wie du beitragen kannst: [CONTRIBUTING.md](../CONTRIBUTING.md)
78+2. Sende Feature Requests und Issues unter Verwendung der bereitgestellten Vorlagen.
79+3. Lies diese gesamte README-Datei und überprüfe zuerst die Dokumentationswebsite, um doppelte Issues zu vermeiden.
80+
81+## Screenshots
82+
83+<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f">
84+<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1">
85+
86+## Charakterkarten
87+
88+SillyTavern basiert auf dem Konzept der "Charakterkarten". Eine Charakterkarte ist eine Sammlung von Prompts, die das Verhalten des LLM festlegen und erforderlich sind, um persistente Gespräche in SillyTavern zu führen. Sie funktionieren ähnlich wie ChatGPT's GPTs oder Poe's Bots. Der Inhalt einer Charakterkarte kann alles sein: ein abstraktes Szenario, ein Assistent, der für eine bestimmte Aufgabe maßgeschneidert ist, eine berühmte Persönlichkeit oder ein fiktiver Charakter.
89+
90+Das Namensfeld ist der einzige erforderliche Eingabewert für die Charakterkarte. Um ein neutrales Gespräch mit dem LLM zu beginnen, erstelle eine neue Karte, die einfach "Assistent" genannt wird, und lasse die restlichen Felder leer. Für einen thematischeren Chat kannst du dem LLM verschiedene Hintergrundinformationen, Verhaltensweisen und Schreibmuster sowie ein Szenario geben, um das Gespräch zu beginnen.
91+
92+Um ein schnelles Gespräch zu führen, ohne eine Charakterkarte auszuwählen, oder um einfach die LLM-Verbindung zu testen, gib einfach dein Prompt in die Eingabezeile auf dem Willkommensbildschirm ein, nachdem du SillyTavern geöffnet hast. Bitte beachte, dass solche Chats vorübergehend sind und nicht gespeichert werden.
93+
94+Um eine allgemeine Vorstellung davon zu bekommen, wie man Charakterkarten definiert, sieh dir die mitgelieferte Charakterkarte (Seraphina) an oder lade ausgewählte von der Community erstellte Karten im Menü "Erweiterungen & Assets herunterladen" herunter.
95+
96+## Wichtigste Features
97+
98+* Erweiterte Text-Generierungs-Einstellungen mit vielen von der Community erstellten mitgelieferten Einstellungen (Presets)
99+* Unterstützung für World Info: Erstelle reichhaltige Lore oder reduziere die Tokens in deiner Charakterkarte
100+* Gruppenchats: Multi-Bot-Räume für Charaktere, die mit dir und/oder untereinander sprechen
101+* Reichhaltige UI-Anpassungsoptionen: Themes zur Farbenwahl, Hintergrundbilder, benutzerdefiniertes CSS und mehr
102+* Benutzer-Personas: Lass die KI ein wenig über dich wissen, um die Immersion zu erhöhen
103+* Eingebaute RAG-Unterstützung: Füge Dokumente zu deinen Chats hinzu, auf die die KI verweisen kann
104+* Umfangreiches "Chat-Befehle"-System und eigene [Scripting-Engine](https://docs.sillytavern.app/usage/st-script/)
105+
106+## Erweiterungen
107+
108+SillyTavern unterstützt Erweiterungen.
109+
110+* Emotionale Ausdrucksformen von Charakteren (Sprites)
111+* Automatische Zusammenfassung des Chatverlaufs
112+* Automatische UI- und Chat-Übersetzung
113+* Bildgenerierung mit Stable Diffusion/FLUX/DALL-E
114+* Text-to-Speech für KI-Antwortnachrichten (über ElevenLabs, Silero oder die TTS-Funktion des Betriebssystems)
115+* Websuchfunktionen zum Hinzufügen zusätzlicher realer Kontexte zu deinen Eingabeaufforderungen
116+* Viele weitere sind im Menü "Erweiterungen & Assets herunterladen" verfügbar.
117+
118+Tutorials zur Nutzung findest du in der [Dokumentation](https://docs.sillytavern.app/).
119+
120+# ⌛ Installation
121+
122+> \[!WARNING]
123+>
124+> * INSTALLIERE NICHT IN EINEM VON WINDOWS KONTROLLIERTEN ORDNER (Programme, System32 usw.).
125+> * FÜHRE START.BAT NICHT MIT ADMIN-BERECHTIGUNGEN AUS.
126+> * DIE INSTALLATION AUF WINDOWS 7 IST UNMÖGLICH, DA ES NODEJS 18.16 NICHT AUSFÜHREN KANN.
127+
128+## 🪟 Windows
129+
130+### Installation über Git
131+
132+1. Installiere [NodeJS](https://nodejs.org/en) (die neueste LTS-Version wird empfohlen).
133+2. Installiere [Git für Windows](https://gitforwindows.org/).
134+3. Öffne den Windows-Explorer (`Win+E`).
135+4. Gehe zu oder erstelle einen Ordner, der nicht von Windows kontrolliert oder überwacht wird. (z.B.: C:\MySpecialFolder\)
136+5. Öffne ein Eingabeaufforderungsfenster in diesem Ordner, indem du in die 'Adressleiste' oben klickst, `cmd` eingibst und Enter drückst.
137+6. Sobald das schwarze Fenster (Eingabeaufforderung) erscheint, gib EINE der folgenden Optionen ein und drücke Enter:
138+
139+* für den Release-Branch: `git clone https://github.com/SillyTavern/SillyTavern -b release`
140+* für den Staging-Branch: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
141+
142+7. Sobald alles geklont ist, doppelklicke auf `Start.bat`, damit NodeJS seine Anforderungen installiert.
143+8. Der Server wird dann gestartet, und SillyTavern wird in deinem Browser geöffnet.
144+
145+### Installation über GitHub Desktop
146+
147+(Dies ermöglicht die Nutzung von git **nur** in GitHub Desktop. Wenn du `git` auch in der Eingabeaufforderung verwenden möchtest, musst du auch [Git für Windows](https://gitforwindows.org/) installieren.)
148+
149+1. Installiere [NodeJS](https://nodejs.org/en) (die neueste LTS-Version wird empfohlen).
150+2. Installiere [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32).
151+3. Klicke nach der Installation von GitHub Desktop auf `Ein Repository aus dem Internet klonen....` (Hinweis: Du **musst kein** GitHub-Konto für diesen Schritt erstellen).
152+4. Klicke im Menü auf den Tab URL, gib diese URL ein `https://github.com/SillyTavern/SillyTavern` und klicke auf Klonen. Du kannst den lokalen Pfad ändern, um festzulegen, wohin SillyTavern heruntergeladen werden soll.
153+5. Um SillyTavern zu öffnen, durchsuche mit dem Windows-Explorer den Ordner, in den du das Repository geklont hast. Standardmäßig wird das Repository hier hin geklont: `C:\Users\[Dein Windows-Benutzername]\Documents\GitHub\SillyTavern`.
154+6. Doppelklicke auf die Datei `start.bat`. (Hinweis: Der Teil `.bat` des Dateinamens könnte von deinem Betriebssystem verborgen sein. In diesem Fall sieht es aus wie eine Datei namens "`Start`". Dies ist die Datei, auf die du doppelklickst, um SillyTavern auszuführen.)
155+7. Nach dem Doppelklicken sollte ein großes schwarzes Konsolenfenster erscheinen, und SillyTavern beginnt, das zu installieren, was es zum Betrieb benötigt.
156+8. Nach dem Installationsprozess sollte das Konsolenfenster so aussehen, und ein SillyTavern-Tab sollte in deinem Browser geöffnet sein.
157+9. Verbinde dich mit einer der [unterstützten APIs](https://docs.sillytavern.app/usage/api-connections/) und beginne zu chatten!
158+
159+## 🐧 Linux & 🍎 MacOS
160+
161+Für MacOS/Linux werden all diese Schritte in einem Terminal durchgeführt.
162+
163+1. Installiere git und nodeJS (die Methode zur Durchführung hängt von deinem Betriebssystem ab).
164+2. Klone das Repository.
165+
166+* für den Release-Branch: `git clone https://github.com/SillyTavern/SillyTavern -b release`
167+* für den Staging-Branch: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
168+
169+3. `cd SillyTavern`, um in den Installationsordner zu navigieren.
170+4. Führe das Skript `start.sh` mit einem dieser Befehle aus:
171+
172+* `./start.sh`
173+* `bash start.sh`
174+
175+## ⚡ Installation über SillyTavern Launcher
176+
177+Der SillyTavern Launcher ist ein Installationsassistent, der dir bei der Einrichtung mit vielen Optionen helfen wird, einschließlich der Installation eines Backends für lokale Inferenz.
178+
179+### Für Windows-Nutzer
180+
181+1. Drücke auf deiner Tastatur **`WINDOWS + R`**, um das Ausführen-Dialogfeld zu öffnen. Führe dann den folgenden Befehl aus, um git zu installieren:
182+
183+```shell
184+cmd /c winget install -e --id Git.Git
185+```
186+
187+2. Drücke auf deiner Tastatur **`WINDOWS + E`**, um den Datei-Explorer zu öffnen, und navigiere dann zu dem Ordner, in dem du den Launcher installieren möchtest. Gib im gewünschten Ordner in die Adressleiste `cmd` ein und drücke Enter. Führe dann den folgenden Befehl aus:
188+
189+```shell
190+git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
191+```
192+
193+### Für Linux-Nutzer
194+
195+1. Öffne dein bevorzugtes Terminal und installiere git.
196+2. Klone den SillyTavern-Launcher mit:
197+
198+```shell
199+git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
200+```
201+
202+3. Starte die installer.sh mit:
203+
204+```shell
205+chmod +x install.sh && ./install.sh
206+```
207+
208+4. Nach der Installation starte die launcher.sh mit:
209+
210+```shell
211+chmod +x launcher.sh && ./launcher.sh
212+```
213+
214+### Für Mac-Nutzer
215+
216+1. Öffne ein Terminal und installiere brew mit:
217+
218+```shell
219+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
220+```
221+
222+2. Installiere git mit:
223+
224+```shell
225+brew install git
226+```
227+
228+3. Klone den SillyTavern-Launcher mit:
229+
230+```shell
231+git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
232+```
233+
234+4. Starte die installer.sh mit:
235+
236+```shell
237+chmod +x install.sh && ./install.sh
238+```
239+
240+5. Nach der Installation starte die launcher.sh mit:
241+
242+```shell
243+chmod +x launcher.sh && ./launcher.sh
244+```
245+
246+## 🐋 Installation über Docker
247+
248+Diese Anweisungen setzen voraus, dass du Docker installiert hast, auf deine Befehlszeile für die Installation von Containern zugreifen kannst und mit deren allgemeiner Funktionsweise vertraut bist.
249+
250+### Image selbst bauen
251+
252+Wir haben einen umfassenden Leitfaden zur Nutzung von SillyTavern in Docker [hier](http://docs.sillytavern.app/installation/docker/) der die Installationen auf Windows, macOS und Linux abdeckt! Lies ihn, wenn du das Image selbst bauen möchtest.
253+
254+### Verwendung der GitHub Container Registry (am einfachsten)
255+
256+Du benötigst zwei zwingende Verzeichniszuordnungen und eine Portzuordnung, um SillyTavern funktionsfähig zu machen. Ersetze in dem Befehl deine Auswahl an den folgenden Stellen:
257+
258+#### Container-Variablen
259+
260+##### Volumen-Zuordnungen
261+
262+* [config] - Das Verzeichnis, in dem die SillyTavern-Konfigurationsdateien auf deinem Host-Computer gespeichert werden
263+* [data] - Das Verzeichnis, in dem die Benutzerdaten von SillyTavern (einschließlich Charaktere) auf deinem Host-Computer gespeichert werden
264+* [plugins] - (optional) Das Verzeichnis, in dem die SillyTavern-Server-Plugins auf deinem Host-Computer gespeichert werden
265+
266+##### Port-Zuordnungen
267+
268+* [PublicPort] - Der Port, über den der Datenverkehr ausgegeben werden soll. Dies ist zwingend erforderlich, da du auf die Instanz von außerhalb des virtuellen Maschinencontainers zugreifst. EXPOSIERE DIES NICHT IM INTERNET, OHNE EINEN GETRENNTEN SERVICE FÜR DIE SICHERHEIT ZU IMPLEMENTIEREN.
269+
270+##### Zusätzliche Einstellungen
271+
272+* [DockerNet] - Das Docker-Netzwerk, mit dem der Container erstellt werden soll. Wenn du nicht weißt, was das ist, sieh dir die [offizielle Docker-Dokumentation](https://docs.docker.com/reference/cli/docker/network/) an.
273+* [version] - Auf der rechten Seite dieser GitHub-Seite siehst du "Packages". Wähle das Paket "sillytavern" und du siehst die Imageversionen. Das Image-Tag "latest" hält dich auf dem Laufenden mit dem aktuellen Release. Du kannst auch "staging" und "release" Tags nutzen, die auf die nightly images der jeweiligen Branche verweisen, aber das könnte unangemessen sein, wenn du Erweiterungen verwendest, die möglicherweise kaputt sind und Zeit benötigen, um aktualisiert zu werden.
274+
275+#### Installationsbefehl
276+
277+1. Öffne deine Befehlszeile.
278+2. Führe den folgenden Befehl aus:
279+
280+`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
281+
282+> Beachte, dass 8000 ein Standard-Listening-Port ist. Vergiss nicht, einen geeigneten Port zu verwenden, wenn du ihn in der Konfiguration änderst.
283+
284+## 📱 Installation über Termux auf Android OS
285+
286+> \[!HINWEIS]
287+> **SillyTavern kann nativ auf Android-Geräten über Termux ausgeführt werden, aber wir bieten keine offizielle Unterstützung für diesen Anwendungsfall.**
288+>
289+> **Bitte beziehe dich auf diesen Leitfaden von ArroganceComplex#2659:**
290+>
291+> * <https://rentry.org/STAI-Termux>
292+
293+**Nicht unterstützte Plattform: android arm LEtime-web.** 32-Bit-Android benötigt eine externe Abhängigkeit, die nicht mit npm installiert werden kann. Verwende den folgenden Befehl, um sie zu installieren: `pkg install esbuild`. Führe dann die üblichen Installationsschritte aus.
294+
295+## API-Schlüsselverwaltung
296+
297+SillyTavern speichert deine API-Schlüssel in einer Datei `secrets.json` im Benutzerdatenverzeichnis (`/data/default-user/secrets.json` ist der Standardpfad).
298+
299+Standardmäßig sind API-Schlüssel nach dem Speichern und Aktualisieren der Seite nicht mehr über die Benutzeroberfläche sichtbar.
300+
301+Um die Ansicht deiner Schlüssel zu aktivieren:
302+
303+1. Setze den Wert von `allowKeysExposure` auf `true` in der Datei `config.yaml`.
304+2. Starte den SillyTavern-Server neu.
305+3. Klicke auf den Link 'Verborgene API-Schlüssel anzeigen' in der unteren rechten Ecke des API-Verbindungsfeldes.
306+
307+## Befehlszeilenargumente
308+
309+Sie können Befehlszeilenargumente an den Start des SillyTavern-Servers übergeben, um einige Einstellungen in `config.yaml` zu überschreiben.
310+
311+### Beispiele
312+
313+```shell
314+node server.js --port 8000 --listen false
315+# oder
316+npm run start -- --port 8000 --listen false
317+# oder (nur Windows)
318+Start.bat --port 8000 --listen false
319+```
320+
321+### Unterstützte Argumente
322+
323+| Option | Beschreibung | Typ |
324+|-------------------------|----------------------------------------------------------------------------------------------------|----------|
325+| `--version` | Versionsnummer anzeigen | boolean |
326+| `--enableIPv6` | Aktiviert IPv6. | boolean |
327+| `--enableIPv4` | Aktiviert IPv4. | boolean |
328+| `--port` | Legt den Port fest, unter dem SillyTavern ausgeführt wird. Wenn nicht angegeben, wird auf YAML-Konfiguration „Port“ zurückgegriffen. | number |
329+| „--dnsPreferIPv6“ | Bevorzugt IPv6 für DNS. Wenn nicht angegeben, wird auf YAML-Konfiguration „preferIPv6“ zurückgegriffen. | boolean |
330+| „--autorun“ | Startet SillyTavern automatisch im Browser. Wenn nicht angegeben, wird auf YAML-Konfiguration „autorun“ zurückgegriffen.| boolean |
331+| „--autorunHostname“ | Der Autorun-Hostname, am besten auf „auto“ belassen. | string |
332+| „--autorunPortOverride“ | Überschreibt den Port für Autorun. | string |
333+| „--listen“ | SillyTavern lauscht auf allen Netzwerkschnittstellen. Wenn nicht angegeben, wird auf YAML-Konfiguration „listen“ zurückgegriffen.| boolean |
334+| „--corsProxy“ | Aktiviert CORS-Proxy. Wenn nicht angegeben, wird auf YAML-Konfiguration „enableCorsProxy“ zurückgegriffen. | boolean |
335+| `--disableCsrf` | Deaktiviert CSRF-Schutz | boolean |
336+| `--ssl` | Aktiviert SSL | boolean |
337+| `--certPath` | Pfad zu Ihrer Zertifikatsdatei. | string |
338+| `--keyPath` | Pfad zu Ihrer privaten Schlüsseldatei. | string |
339+| `--whitelist` | Aktiviert den Whitelist-Modus | boolean |
340+| `--dataRoot` | Stammverzeichnis für Datenspeicherung | string |
341+| `--avoidLocalhost` | Vermeidet die Verwendung von „localhost“ für Autorun im Auto-Modus. | boolean |
342+| `--basicAuthMode` | Aktiviert die grundlegende Authentifizierung | boolean |
343+| `--requestProxyEnabled` | Aktiviert die Verwendung eines Proxys für ausgehende Anfragen | boolean |
344+| `--requestProxyUrl` | Proxy-URL anfordern (HTTP- oder SOCKS-Protokolle) | string |
345+| `--requestProxyBypass` | Proxy-Bypass-Liste anfordern (durch Leerzeichen getrennte Liste von Hosts) | Array |
346+
347+## Remoteverbindungen
348+
349+Dies ist in den meisten Fällen für Personen gedacht, die SillyTavern auf ihren Mobiltelefonen verwenden möchten, während ihr PC den ST-Server im selben WLAN-Netzwerk betreibt. Es kann jedoch auch verwendet werden, um Remoteverbindungen von überall her zu ermöglichen.
350+
351+Lies die ausführliche Anleitung zum Einrichten von Remoteverbindungen in den [Docs](https://docs.sillytavern.app/usage/remoteconnections/).
352+
353+Möglicherweise möchtest du SillyTavern-Benutzerprofile auch mit (optionalem) Kennwortschutz konfigurieren: [Benutzer](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users).
354+
355+## Leistungsprobleme?
356+
357+1. Deaktiviere den Unschärfeeffekt und aktiviere "Verringerte Bewegung" im Bedienfeld "Benutzereinstellungen" (UI-Design schaltet Kategorie um).
358+2. Wenn du Response Streaming verwendest, stelle die Streaming-FPS auf einen niedrigeren Wert ein (10-15 FPS werden empfohlen).
359+3. Stelle sicher, dass der Browser die GPU-Beschleunigung zum Rendern verwenden kann.
360+
361+## Lizenz und Danksagungen
362+
363+**Dieses Programm wird in der Hoffnung verbreitet, dass es nützlich ist,
364+aber OHNE JEGLICHE GARANTIE; nicht einmal die stillschweigende Garantie der
365+MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Siehe die
366+GNU Affero General Public License für weitere Details.**
367+
368+* [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8 von Humi: MIT-Lizenz
369+* Teile von CncAnons TavernAITurbo-Mod werden mit Genehmigung verwendet
370+* Visual Novel-Modus inspiriert von der Arbeit von PepperTaco (<https://github.com/peppertaco/Tavern/>)
371+* Noto Sans-Schriftart von Google (OFL-Lizenz)
372+* Symboldesign von Font Awesome <https://fontawesome.com> (Symbole: CC BY 4.0, Schriftarten: SIL OFL 1.1, Code: MIT-Lizenz)
373+* Standardinhalt von @OtisAlejandro (Seraphina-Charakter und Lorebook) und @kallmeflocc (10.000 Discord-Benutzer-Feierhintergrund)
374+* Docker-Anleitung von [@mrguymiah](https://github.com/mrguymiah) und [@Bronya-Rand](https://github.com/Bronya-Rand)
375+
376+## Top Contributors
377+
378+[![Contributors](https://contrib.rocks/image?repo=SillyTavern/SillyTavern)](https://github.com/SillyTavern/SillyTavern/graphs/contributors)
379+
380+<!-- LINK GROUP -->
381+[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44
382+[discord-link]: https://discord.gg/sillytavern
383+[discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge
.github/readme-ja_jp.md+1 -1
@@ -5,7 +5,7 @@
55
66<div align="center">
77
88[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md)
99
1010[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
1111[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
.github/readme-ru_ru.md+1 -1
@@ -7,7 +7,7 @@
77
88<div align="center">
99
1010[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский
1111
1212[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
1313[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
.github/readme-zh_cn.md+1 -1
@@ -5,7 +5,7 @@
55
66<div align="center">
77
88[English](readme.md) | [German](readme-de_de.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
99
1010[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
1111[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
.github/readme.md+1 -1
@@ -4,7 +4,7 @@
44
55<div align="center">
66
77English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
88
99[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
1010[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)
public/css/popup.css+2 -1
@@ -1,3 +1,4 @@
1+@import url('/lib/dialog-polyfill.css');
12@import url('./popup-safari-fix.css');
23
34dialog {
@@ -42,7 +43,7 @@ dialog {
4243 display: flex;
4344 flex-direction: column;
4445 overflow: hidden;
4546 width: min(100%, 100vw);
4647 height: 100%;
4748 padding: 1px;
4849}
public/index.html+1 -0
@@ -2888,6 +2888,7 @@
28882888 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
28892889 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
28902890 <option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>
2891+ <option value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</option>
28912892 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
28922893 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
28932894 <option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option>
public/lib/dialog-polyfill.css+37 -0
@@ -0,0 +1,37 @@
1+.poly_dialog {
2+ position: absolute;
3+ left: 0; right: 0;
4+ width: -moz-fit-content;
5+ width: -webkit-fit-content;
6+ width: fit-content;
7+ height: -moz-fit-content;
8+ height: -webkit-fit-content;
9+ height: fit-content;
10+ margin: auto;
11+ border: solid;
12+ padding: 1em;
13+ background: white;
14+ color: black;
15+ display: block;
16+}
17+
18+.poly_dialog:not([open]) {
19+ display: none;
20+}
21+
22+.poly_dialog + .backdrop {
23+ position: fixed;
24+ top: 0; right: 0; bottom: 0; left: 0;
25+ background: rgba(0,0,0,0.1);
26+}
27+
28+._poly_dialog_overlay {
29+ position: fixed;
30+ top: 0; right: 0; bottom: 0; left: 0;
31+}
32+
33+.poly_dialog.fixed {
34+ position: fixed;
35+ top: 50%;
36+ transform: translate(0, -50%);
37+}
37 \ No newline at end of file
public/lib/dialog-polyfill.esm.js+858 -0
@@ -0,0 +1,858 @@
1+// nb. This is for IE10 and lower _only_.
2+var supportCustomEvent = window.CustomEvent;
3+if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
4+ supportCustomEvent = function CustomEvent(event, x) {
5+ x = x || {};
6+ var ev = document.createEvent('CustomEvent');
7+ ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
8+ return ev;
9+ };
10+ supportCustomEvent.prototype = window.Event.prototype;
11+}
12+
13+/**
14+ * Dispatches the passed event to both an "on<type>" handler as well as via the
15+ * normal dispatch operation. Does not bubble.
16+ *
17+ * @param {!EventTarget} target
18+ * @param {!Event} event
19+ * @return {boolean}
20+ */
21+function safeDispatchEvent(target, event) {
22+ var check = 'on' + event.type.toLowerCase();
23+ if (typeof target[check] === 'function') {
24+ target[check](event);
25+ }
26+ return target.dispatchEvent(event);
27+}
28+
29+/**
30+ * @param {Element} el to check for stacking context
31+ * @return {boolean} whether this el or its parents creates a stacking context
32+ */
33+function createsStackingContext(el) {
34+ while (el && el !== document.body) {
35+ var s = window.getComputedStyle(el);
36+ var invalid = function(k, ok) {
37+ return !(s[k] === undefined || s[k] === ok);
38+ };
39+
40+ if (s.opacity < 1 ||
41+ invalid('zIndex', 'auto') ||
42+ invalid('transform', 'none') ||
43+ invalid('mixBlendMode', 'normal') ||
44+ invalid('filter', 'none') ||
45+ invalid('perspective', 'none') ||
46+ s['isolation'] === 'isolate' ||
47+ s.position === 'fixed' ||
48+ s.webkitOverflowScrolling === 'touch') {
49+ return true;
50+ }
51+ el = el.parentElement;
52+ }
53+ return false;
54+}
55+
56+/**
57+ * Finds the nearest <dialog> from the passed element.
58+ *
59+ * @param {Element} el to search from
60+ * @return {HTMLDialogElement} dialog found
61+ */
62+function findNearestDialog(el) {
63+ while (el) {
64+ if (el.localName === 'dialog') {
65+ return /** @type {HTMLDialogElement} */ (el);
66+ }
67+ if (el.parentElement) {
68+ el = el.parentElement;
69+ } else if (el.parentNode) {
70+ el = el.parentNode.host;
71+ } else {
72+ el = null;
73+ }
74+ }
75+ return null;
76+}
77+
78+/**
79+ * Blur the specified element, as long as it's not the HTML body element.
80+ * This works around an IE9/10 bug - blurring the body causes Windows to
81+ * blur the whole application.
82+ *
83+ * @param {Element} el to blur
84+ */
85+function safeBlur(el) {
86+ // Find the actual focused element when the active element is inside a shadow root
87+ while (el && el.shadowRoot && el.shadowRoot.activeElement) {
88+ el = el.shadowRoot.activeElement;
89+ }
90+
91+ if (el && el.blur && el !== document.body) {
92+ el.blur();
93+ }
94+}
95+
96+/**
97+ * @param {!NodeList} nodeList to search
98+ * @param {Node} node to find
99+ * @return {boolean} whether node is inside nodeList
100+ */
101+function inNodeList(nodeList, node) {
102+ for (var i = 0; i < nodeList.length; ++i) {
103+ if (nodeList[i] === node) {
104+ return true;
105+ }
106+ }
107+ return false;
108+}
109+
110+/**
111+ * @param {HTMLFormElement} el to check
112+ * @return {boolean} whether this form has method="dialog"
113+ */
114+function isFormMethodDialog(el) {
115+ if (!el || !el.hasAttribute('method')) {
116+ return false;
117+ }
118+ return el.getAttribute('method').toLowerCase() === 'dialog';
119+}
120+
121+/**
122+ * @param {!DocumentFragment|!Element} hostElement
123+ * @return {?Element}
124+ */
125+function findFocusableElementWithin(hostElement) {
126+ // Note that this is 'any focusable area'. This list is probably not exhaustive, but the
127+ // alternative involves stepping through and trying to focus everything.
128+ var opts = ['button', 'input', 'keygen', 'select', 'textarea'];
129+ var query = opts.map(function(el) {
130+ return el + ':not([disabled])';
131+ });
132+ // TODO(samthor): tabindex values that are not numeric are not focusable.
133+ query.push('[tabindex]:not([disabled]):not([tabindex=""])'); // tabindex != "", not disabled
134+ var target = hostElement.querySelector(query.join(', '));
135+
136+ if (!target && 'attachShadow' in Element.prototype) {
137+ // If we haven't found a focusable target, see if the host element contains an element
138+ // which has a shadowRoot.
139+ // Recursively search for the first focusable item in shadow roots.
140+ var elems = hostElement.querySelectorAll('*');
141+ for (var i = 0; i < elems.length; i++) {
142+ if (elems[i].tagName && elems[i].shadowRoot) {
143+ target = findFocusableElementWithin(elems[i].shadowRoot);
144+ if (target) {
145+ break;
146+ }
147+ }
148+ }
149+ }
150+ return target;
151+}
152+
153+/**
154+ * Determines if an element is attached to the DOM.
155+ * @param {Element} element to check
156+ * @return {boolean} whether the element is in DOM
157+ */
158+function isConnected(element) {
159+ return element.isConnected || document.body.contains(element);
160+}
161+
162+/**
163+ * @param {!Event} event
164+ * @return {?Element}
165+ */
166+function findFormSubmitter(event) {
167+ if (event.submitter) {
168+ return event.submitter;
169+ }
170+
171+ var form = event.target;
172+ if (!(form instanceof HTMLFormElement)) {
173+ return null;
174+ }
175+
176+ var submitter = dialogPolyfill.formSubmitter;
177+ if (!submitter) {
178+ var target = event.target;
179+ var root = ('getRootNode' in target && target.getRootNode() || document);
180+ submitter = root.activeElement;
181+ }
182+
183+ if (!submitter || submitter.form !== form) {
184+ return null;
185+ }
186+ return submitter;
187+}
188+
189+/**
190+ * @param {!Event} event
191+ */
192+function maybeHandleSubmit(event) {
193+ if (event.defaultPrevented) {
194+ return;
195+ }
196+ var form = /** @type {!HTMLFormElement} */ (event.target);
197+
198+ // We'd have a value if we clicked on an imagemap.
199+ var value = dialogPolyfill.imagemapUseValue;
200+ var submitter = findFormSubmitter(event);
201+ if (value === null && submitter) {
202+ value = submitter.value;
203+ }
204+
205+ // There should always be a dialog as this handler is added specifically on them, but check just
206+ // in case.
207+ var dialog = findNearestDialog(form);
208+ if (!dialog) {
209+ return;
210+ }
211+
212+ // Prefer formmethod on the button.
213+ var formmethod = submitter && submitter.getAttribute('formmethod') || form.getAttribute('method');
214+ if (formmethod !== 'dialog') {
215+ return;
216+ }
217+ event.preventDefault();
218+
219+ if (value != null) {
220+ // nb. we explicitly check against null/undefined
221+ dialog.close(value);
222+ } else {
223+ dialog.close();
224+ }
225+}
226+
227+/**
228+ * @param {!HTMLDialogElement} dialog to upgrade
229+ * @constructor
230+ */
231+function dialogPolyfillInfo(dialog) {
232+ this.dialog_ = dialog;
233+ this.replacedStyleTop_ = false;
234+ this.openAsModal_ = false;
235+
236+ // Set a11y role. Browsers that support dialog implicitly know this already.
237+ if (!dialog.hasAttribute('role')) {
238+ dialog.setAttribute('role', 'dialog');
239+ }
240+
241+ dialog.show = this.show.bind(this);
242+ dialog.showModal = this.showModal.bind(this);
243+ dialog.close = this.close.bind(this);
244+
245+ dialog.addEventListener('submit', maybeHandleSubmit, false);
246+
247+ if (!('returnValue' in dialog)) {
248+ dialog.returnValue = '';
249+ }
250+
251+ if ('MutationObserver' in window) {
252+ var mo = new MutationObserver(this.maybeHideModal.bind(this));
253+ mo.observe(dialog, {attributes: true, attributeFilter: ['open']});
254+ } else {
255+ // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also
256+ // seem to fire even if the element was removed as part of a parent removal. Use the removed
257+ // events to force downgrade (useful if removed/immediately added).
258+ var removed = false;
259+ var cb = function() {
260+ removed ? this.downgradeModal() : this.maybeHideModal();
261+ removed = false;
262+ }.bind(this);
263+ var timeout;
264+ var delayModel = function(ev) {
265+ if (ev.target !== dialog) { return; } // not for a child element
266+ var cand = 'DOMNodeRemoved';
267+ removed |= (ev.type.substr(0, cand.length) === cand);
268+ window.clearTimeout(timeout);
269+ timeout = window.setTimeout(cb, 0);
270+ };
271+ ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {
272+ dialog.addEventListener(name, delayModel);
273+ });
274+ }
275+ // Note that the DOM is observed inside DialogManager while any dialog
276+ // is being displayed as a modal, to catch modal removal from the DOM.
277+
278+ Object.defineProperty(dialog, 'open', {
279+ set: this.setOpen.bind(this),
280+ get: dialog.hasAttribute.bind(dialog, 'open')
281+ });
282+
283+ this.backdrop_ = document.createElement('div');
284+ this.backdrop_.className = 'backdrop';
285+ this.backdrop_.addEventListener('mouseup' , this.backdropMouseEvent_.bind(this));
286+ this.backdrop_.addEventListener('mousedown', this.backdropMouseEvent_.bind(this));
287+ this.backdrop_.addEventListener('click' , this.backdropMouseEvent_.bind(this));
288+}
289+
290+dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({
291+
292+ get dialog() {
293+ return this.dialog_;
294+ },
295+
296+ /**
297+ * Maybe remove this dialog from the modal top layer. This is called when
298+ * a modal dialog may no longer be tenable, e.g., when the dialog is no
299+ * longer open or is no longer part of the DOM.
300+ */
301+ maybeHideModal: function() {
302+ if (this.dialog_.hasAttribute('open') && isConnected(this.dialog_)) { return; }
303+ this.downgradeModal();
304+ },
305+
306+ /**
307+ * Remove this dialog from the modal top layer, leaving it as a non-modal.
308+ */
309+ downgradeModal: function() {
310+ if (!this.openAsModal_) { return; }
311+ this.openAsModal_ = false;
312+ this.dialog_.style.zIndex = '';
313+
314+ // This won't match the native <dialog> exactly because if the user set top on a centered
315+ // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's
316+ // possible to polyfill this perfectly.
317+ if (this.replacedStyleTop_) {
318+ this.dialog_.style.top = '';
319+ this.replacedStyleTop_ = false;
320+ }
321+
322+ // Clear the backdrop and remove from the manager.
323+ this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);
324+ dialogPolyfill.dm.removeDialog(this);
325+ },
326+
327+ /**
328+ * @param {boolean} value whether to open or close this dialog
329+ */
330+ setOpen: function(value) {
331+ if (value) {
332+ this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');
333+ } else {
334+ this.dialog_.removeAttribute('open');
335+ this.maybeHideModal(); // nb. redundant with MutationObserver
336+ }
337+ },
338+
339+ /**
340+ * Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if
341+ * they were on the dialog itself.
342+ *
343+ * @param {!Event} e to redirect
344+ */
345+ backdropMouseEvent_: function(e) {
346+ if (!this.dialog_.hasAttribute('tabindex')) {
347+ // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be
348+ // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this
349+ // would not be needed - clicks would move the implicit cursor there.
350+ var fake = document.createElement('div');
351+ this.dialog_.insertBefore(fake, this.dialog_.firstChild);
352+ fake.tabIndex = -1;
353+ fake.focus();
354+ this.dialog_.removeChild(fake);
355+ } else {
356+ this.dialog_.focus();
357+ }
358+
359+ var redirectedEvent = document.createEvent('MouseEvents');
360+ redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
361+ e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
362+ e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
363+ this.dialog_.dispatchEvent(redirectedEvent);
364+ e.stopPropagation();
365+ },
366+
367+ /**
368+ * Focuses on the first focusable element within the dialog. This will always blur the current
369+ * focus, even if nothing within the dialog is found.
370+ */
371+ focus_: function() {
372+ // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.
373+ var target = this.dialog_.querySelector('[autofocus]:not([disabled])');
374+ if (!target && this.dialog_.tabIndex >= 0) {
375+ target = this.dialog_;
376+ }
377+ if (!target) {
378+ target = findFocusableElementWithin(this.dialog_);
379+ }
380+ safeBlur(document.activeElement);
381+ target && target.focus();
382+ },
383+
384+ /**
385+ * Sets the zIndex for the backdrop and dialog.
386+ *
387+ * @param {number} dialogZ
388+ * @param {number} backdropZ
389+ */
390+ updateZIndex: function(dialogZ, backdropZ) {
391+ if (dialogZ < backdropZ) {
392+ throw new Error('dialogZ should never be < backdropZ');
393+ }
394+ this.dialog_.style.zIndex = dialogZ;
395+ this.backdrop_.style.zIndex = backdropZ;
396+ },
397+
398+ /**
399+ * Shows the dialog. If the dialog is already open, this does nothing.
400+ */
401+ show: function() {
402+ if (!this.dialog_.open) {
403+ this.setOpen(true);
404+ this.focus_();
405+ }
406+ },
407+
408+ /**
409+ * Show this dialog modally.
410+ */
411+ showModal: function() {
412+ if (this.dialog_.hasAttribute('open')) {
413+ throw new Error('Failed to execute \'showModal\' on dialog: The element is already open, and therefore cannot be opened modally.');
414+ }
415+ if (!isConnected(this.dialog_)) {
416+ throw new Error('Failed to execute \'showModal\' on dialog: The element is not in a Document.');
417+ }
418+ if (!dialogPolyfill.dm.pushDialog(this)) {
419+ throw new Error('Failed to execute \'showModal\' on dialog: There are too many open modal dialogs.');
420+ }
421+
422+ if (createsStackingContext(this.dialog_.parentElement)) {
423+ console.warn('A dialog is being shown inside a stacking context. ' +
424+ 'This may cause it to be unusable. For more information, see this link: ' +
425+ 'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');
426+ }
427+
428+ this.setOpen(true);
429+ this.openAsModal_ = true;
430+
431+ // Optionally center vertically, relative to the current viewport.
432+ if (dialogPolyfill.needsCentering(this.dialog_)) {
433+ dialogPolyfill.reposition(this.dialog_);
434+ this.replacedStyleTop_ = true;
435+ } else {
436+ this.replacedStyleTop_ = false;
437+ }
438+
439+ // Insert backdrop.
440+ this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);
441+
442+ // Focus on whatever inside the dialog.
443+ this.focus_();
444+ },
445+
446+ /**
447+ * Closes this HTMLDialogElement. This is optional vs clearing the open
448+ * attribute, however this fires a 'close' event.
449+ *
450+ * @param {string=} opt_returnValue to use as the returnValue
451+ */
452+ close: function(opt_returnValue) {
453+ if (!this.dialog_.hasAttribute('open')) {
454+ throw new Error('Failed to execute \'close\' on dialog: The element does not have an \'open\' attribute, and therefore cannot be closed.');
455+ }
456+ this.setOpen(false);
457+
458+ // Leave returnValue untouched in case it was set directly on the element
459+ if (opt_returnValue !== undefined) {
460+ this.dialog_.returnValue = opt_returnValue;
461+ }
462+
463+ // Triggering "close" event for any attached listeners on the <dialog>.
464+ var closeEvent = new supportCustomEvent('close', {
465+ bubbles: false,
466+ cancelable: false
467+ });
468+ safeDispatchEvent(this.dialog_, closeEvent);
469+ }
470+
471+});
472+
473+var dialogPolyfill = {};
474+
475+dialogPolyfill.reposition = function(element) {
476+ var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
477+ var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
478+ element.style.top = Math.max(scrollTop, topValue) + 'px';
479+};
480+
481+dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
482+ for (var i = 0; i < document.styleSheets.length; ++i) {
483+ var styleSheet = document.styleSheets[i];
484+ var cssRules = null;
485+ // Some browsers throw on cssRules.
486+ try {
487+ cssRules = styleSheet.cssRules;
488+ } catch (e) {}
489+ if (!cssRules) { continue; }
490+ for (var j = 0; j < cssRules.length; ++j) {
491+ var rule = cssRules[j];
492+ var selectedNodes = null;
493+ // Ignore errors on invalid selector texts.
494+ try {
495+ selectedNodes = document.querySelectorAll(rule.selectorText);
496+ } catch(e) {}
497+ if (!selectedNodes || !inNodeList(selectedNodes, element)) {
498+ continue;
499+ }
500+ var cssTop = rule.style.getPropertyValue('top');
501+ var cssBottom = rule.style.getPropertyValue('bottom');
502+ if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {
503+ return true;
504+ }
505+ }
506+ }
507+ return false;
508+};
509+
510+dialogPolyfill.needsCentering = function(dialog) {
511+ var computedStyle = window.getComputedStyle(dialog);
512+ if (computedStyle.position !== 'absolute') {
513+ return false;
514+ }
515+
516+ // We must determine whether the top/bottom specified value is non-auto. In
517+ // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
518+ // Firefox returns the used value. So we do this crazy thing instead: check
519+ // the inline style and then go through CSS rules.
520+ if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||
521+ (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {
522+ return false;
523+ }
524+ return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
525+};
526+
527+/**
528+ * @param {!Element} element to force upgrade
529+ */
530+dialogPolyfill.forceRegisterDialog = function(element) {
531+ if (window.HTMLDialogElement || element.showModal) {
532+ console.warn('This browser already supports <dialog>, the polyfill ' +
533+ 'may not work correctly', element);
534+ }
535+ if (element.localName !== 'dialog') {
536+ throw new Error('Failed to register dialog: The element is not a dialog.');
537+ }
538+ new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));
539+};
540+
541+/**
542+ * @param {!Element} element to upgrade, if necessary
543+ */
544+dialogPolyfill.registerDialog = function(element) {
545+ if (!element.showModal) {
546+ dialogPolyfill.forceRegisterDialog(element);
547+ }
548+};
549+
550+/**
551+ * @constructor
552+ */
553+dialogPolyfill.DialogManager = function() {
554+ /** @type {!Array<!dialogPolyfillInfo>} */
555+ this.pendingDialogStack = [];
556+
557+ var checkDOM = this.checkDOM_.bind(this);
558+
559+ // The overlay is used to simulate how a modal dialog blocks the document.
560+ // The blocking dialog is positioned on top of the overlay, and the rest of
561+ // the dialogs on the pending dialog stack are positioned below it. In the
562+ // actual implementation, the modal dialog stacking is controlled by the
563+ // top layer, where z-index has no effect.
564+ this.overlay = document.createElement('div');
565+ this.overlay.className = '_poly_dialog_overlay';
566+ this.overlay.addEventListener('click', function(e) {
567+ this.forwardTab_ = undefined;
568+ e.stopPropagation();
569+ checkDOM([]); // sanity-check DOM
570+ }.bind(this));
571+
572+ this.handleKey_ = this.handleKey_.bind(this);
573+ this.handleFocus_ = this.handleFocus_.bind(this);
574+
575+ this.zIndexLow_ = 100000;
576+ this.zIndexHigh_ = 100000 + 150;
577+
578+ this.forwardTab_ = undefined;
579+
580+ if ('MutationObserver' in window) {
581+ this.mo_ = new MutationObserver(function(records) {
582+ var removed = [];
583+ records.forEach(function(rec) {
584+ for (var i = 0, c; c = rec.removedNodes[i]; ++i) {
585+ if (!(c instanceof Element)) {
586+ continue;
587+ } else if (c.localName === 'dialog') {
588+ removed.push(c);
589+ }
590+ removed = removed.concat(c.querySelectorAll('dialog'));
591+ }
592+ });
593+ removed.length && checkDOM(removed);
594+ });
595+ }
596+};
597+
598+/**
599+ * Called on the first modal dialog being shown. Adds the overlay and related
600+ * handlers.
601+ */
602+dialogPolyfill.DialogManager.prototype.blockDocument = function() {
603+ document.documentElement.addEventListener('focus', this.handleFocus_, true);
604+ document.addEventListener('keydown', this.handleKey_);
605+ this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});
606+};
607+
608+/**
609+ * Called on the first modal dialog being removed, i.e., when no more modal
610+ * dialogs are visible.
611+ */
612+dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
613+ document.documentElement.removeEventListener('focus', this.handleFocus_, true);
614+ document.removeEventListener('keydown', this.handleKey_);
615+ this.mo_ && this.mo_.disconnect();
616+};
617+
618+/**
619+ * Updates the stacking of all known dialogs.
620+ */
621+dialogPolyfill.DialogManager.prototype.updateStacking = function() {
622+ var zIndex = this.zIndexHigh_;
623+
624+ for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
625+ dpi.updateZIndex(--zIndex, --zIndex);
626+ if (i === 0) {
627+ this.overlay.style.zIndex = --zIndex;
628+ }
629+ }
630+
631+ // Make the overlay a sibling of the dialog itself.
632+ var last = this.pendingDialogStack[0];
633+ if (last) {
634+ var p = last.dialog.parentNode || document.body;
635+ p.appendChild(this.overlay);
636+ } else if (this.overlay.parentNode) {
637+ this.overlay.parentNode.removeChild(this.overlay);
638+ }
639+};
640+
641+/**
642+ * @param {Element} candidate to check if contained or is the top-most modal dialog
643+ * @return {boolean} whether candidate is contained in top dialog
644+ */
645+dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {
646+ while (candidate = findNearestDialog(candidate)) {
647+ for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
648+ if (dpi.dialog === candidate) {
649+ return i === 0; // only valid if top-most
650+ }
651+ }
652+ candidate = candidate.parentElement;
653+ }
654+ return false;
655+};
656+
657+dialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {
658+ var target = event.composedPath ? event.composedPath()[0] : event.target;
659+
660+ if (this.containedByTopDialog_(target)) { return; }
661+
662+ if (document.activeElement === document.documentElement) { return; }
663+
664+ event.preventDefault();
665+ event.stopPropagation();
666+ safeBlur(/** @type {Element} */ (target));
667+
668+ if (this.forwardTab_ === undefined) { return; } // move focus only from a tab key
669+
670+ var dpi = this.pendingDialogStack[0];
671+ var dialog = dpi.dialog;
672+ var position = dialog.compareDocumentPosition(target);
673+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
674+ if (this.forwardTab_) {
675+ // forward
676+ dpi.focus_();
677+ } else if (target !== document.documentElement) {
678+ // backwards if we're not already focused on <html>
679+ document.documentElement.focus();
680+ }
681+ }
682+
683+ return false;
684+};
685+
686+dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {
687+ this.forwardTab_ = undefined;
688+ if (event.keyCode === 27) {
689+ event.preventDefault();
690+ event.stopPropagation();
691+ var cancelEvent = new supportCustomEvent('cancel', {
692+ bubbles: false,
693+ cancelable: true
694+ });
695+ var dpi = this.pendingDialogStack[0];
696+ if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) {
697+ dpi.dialog.close();
698+ }
699+ } else if (event.keyCode === 9) {
700+ this.forwardTab_ = !event.shiftKey;
701+ }
702+};
703+
704+/**
705+ * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are
706+ * removed and immediately readded don't stay modal, they become normal.
707+ *
708+ * @param {!Array<!HTMLDialogElement>} removed that have definitely been removed
709+ */
710+dialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {
711+ // This operates on a clone because it may cause it to change. Each change also calls
712+ // updateStacking, which only actually needs to happen once. But who removes many modal dialogs
713+ // at a time?!
714+ var clone = this.pendingDialogStack.slice();
715+ clone.forEach(function(dpi) {
716+ if (removed.indexOf(dpi.dialog) !== -1) {
717+ dpi.downgradeModal();
718+ } else {
719+ dpi.maybeHideModal();
720+ }
721+ });
722+};
723+
724+/**
725+ * @param {!dialogPolyfillInfo} dpi
726+ * @return {boolean} whether the dialog was allowed
727+ */
728+dialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {
729+ var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;
730+ if (this.pendingDialogStack.length >= allowed) {
731+ return false;
732+ }
733+ if (this.pendingDialogStack.unshift(dpi) === 1) {
734+ this.blockDocument();
735+ }
736+ this.updateStacking();
737+ return true;
738+};
739+
740+/**
741+ * @param {!dialogPolyfillInfo} dpi
742+ */
743+dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {
744+ var index = this.pendingDialogStack.indexOf(dpi);
745+ if (index === -1) { return; }
746+
747+ this.pendingDialogStack.splice(index, 1);
748+ if (this.pendingDialogStack.length === 0) {
749+ this.unblockDocument();
750+ }
751+ this.updateStacking();
752+};
753+
754+dialogPolyfill.dm = new dialogPolyfill.DialogManager();
755+dialogPolyfill.formSubmitter = null;
756+dialogPolyfill.imagemapUseValue = null;
757+
758+/**
759+ * Installs global handlers, such as click listers and native method overrides. These are needed
760+ * even if a no dialog is registered, as they deal with <form method="dialog">.
761+ */
762+if (window.HTMLDialogElement === undefined) {
763+
764+ /**
765+ * If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with
766+ * one that returns the correct value.
767+ */
768+ var testForm = document.createElement('form');
769+ testForm.setAttribute('method', 'dialog');
770+ if (testForm.method !== 'dialog') {
771+ var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');
772+ if (methodDescriptor) {
773+ // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything
774+ // and don't bother to update the element.
775+ var realGet = methodDescriptor.get;
776+ methodDescriptor.get = function() {
777+ if (isFormMethodDialog(this)) {
778+ return 'dialog';
779+ }
780+ return realGet.call(this);
781+ };
782+ var realSet = methodDescriptor.set;
783+ /** @this {HTMLElement} */
784+ methodDescriptor.set = function(v) {
785+ if (typeof v === 'string' && v.toLowerCase() === 'dialog') {
786+ return this.setAttribute('method', v);
787+ }
788+ return realSet.call(this, v);
789+ };
790+ Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);
791+ }
792+ }
793+
794+ /**
795+ * Global 'click' handler, to capture the <input type="submit"> or <button> element which has
796+ * submitted a <form method="dialog">. Needed as Safari and others don't report this inside
797+ * document.activeElement.
798+ */
799+ document.addEventListener('click', function(ev) {
800+ dialogPolyfill.formSubmitter = null;
801+ dialogPolyfill.imagemapUseValue = null;
802+ if (ev.defaultPrevented) { return; } // e.g. a submit which prevents default submission
803+
804+ var target = /** @type {Element} */ (ev.target);
805+ if ('composedPath' in ev) {
806+ var path = ev.composedPath();
807+ target = path.shift() || target;
808+ }
809+ if (!target || !isFormMethodDialog(target.form)) { return; }
810+
811+ var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);
812+ if (!valid) {
813+ if (!(target.localName === 'input' && target.type === 'image')) { return; }
814+ // this is a <input type="image">, which can submit forms
815+ dialogPolyfill.imagemapUseValue = ev.offsetX + ',' + ev.offsetY;
816+ }
817+
818+ var dialog = findNearestDialog(target);
819+ if (!dialog) { return; }
820+
821+ dialogPolyfill.formSubmitter = target;
822+
823+ }, false);
824+
825+ /**
826+ * Global 'submit' handler. This handles submits of `method="dialog"` which are invalid, i.e.,
827+ * outside a dialog. They get prevented.
828+ */
829+ document.addEventListener('submit', function(ev) {
830+ var form = ev.target;
831+ var dialog = findNearestDialog(form);
832+ if (dialog) {
833+ return; // ignore, handle there
834+ }
835+
836+ var submitter = findFormSubmitter(ev);
837+ var formmethod = submitter && submitter.getAttribute('formmethod') || form.getAttribute('method');
838+ if (formmethod === 'dialog') {
839+ ev.preventDefault();
840+ }
841+ });
842+
843+ /**
844+ * Replace the native HTMLFormElement.submit() method, as it won't fire the
845+ * submit event and give us a chance to respond.
846+ */
847+ var nativeFormSubmit = HTMLFormElement.prototype.submit;
848+ var replacementFormSubmit = function () {
849+ if (!isFormMethodDialog(this)) {
850+ return nativeFormSubmit.call(this);
851+ }
852+ var dialog = findNearestDialog(this);
853+ dialog && dialog.close();
854+ };
855+ HTMLFormElement.prototype.submit = replacementFormSubmit;
856+}
857+
858+export default dialogPolyfill;
public/script.js+5 -2
@@ -1884,6 +1884,7 @@ export async function reloadCurrentChat() {
18841884export async function sendTextareaMessage() {
18851885 if (is_send_press) return;
18861886 if (isExecutingCommandsFromChatInput) return;
1887+ if (this_edit_mes_id) return; // don't proceed if editing a message
18871888
18881889 let generateType;
18891890 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last
@@ -10054,6 +10055,8 @@ jQuery(async function () {
1005410055 }
1005510056
1005610057 else if (id == 'option_continue') {
10058+ if (this_edit_mes_id) return; // don't proceed if editing a message
10059+
1005710060 if (is_send_press == false || fromSlashCommand) {
1005810061 is_send_press = true;
1005910062 Generate('continue', buildOrFillAdditionalArgs());
@@ -10720,7 +10723,7 @@ jQuery(async function () {
1072010723 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
1072110724 await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
1072210725 });
1072310726 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
1072410727 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
1072510728 icon.toggleClass('openIcon closedIcon');
1072610729 drawer.toggleClass('openDrawer closedDrawer');
@@ -10806,7 +10809,7 @@ jQuery(async function () {
1080610809 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', function () {
1080710810 $(this).closest('.drawer-content').removeClass('resizing');
1080810811 });
1080910812 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
1081010813 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
1081110814
1081210815 }
public/scripts/RossAscends-mods.js+19 -3
@@ -45,8 +45,11 @@ var LPanelPin = document.getElementById('lm_button_panel_pin');
4545var WIPanelPin = document.getElementById('WI_panel_pin');
4646
4747var RightNavPanel = document.getElementById('right-nav-panel');
48+var RightNavDrawerIcon = document.getElementById('rightNavDrawerIcon');
4849var LeftNavPanel = document.getElementById('left-nav-panel');
50+var LeftNavDrawerIcon = document.getElementById('leftNavDrawerIcon');
4951var WorldInfo = document.getElementById('WorldInfo');
52+var WIDrawerIcon = document.getElementById('WIDrawerIcon');
5053
5154var SelectedCharacterTab = document.getElementById('rm_button_selected_ch');
5255
@@ -761,13 +764,15 @@ export function initRossMods() {
761764 if ($(RPanelPin).prop('checked') == true) {
762765 //console.log('adding pin class to right nav');
763766 $(RightNavPanel).addClass('pinnedOpen');
767+ $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
764768 } else {
765769 //console.log('removing pin class from right nav');
766770 $(RightNavPanel).removeClass('pinnedOpen');
771+ $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
767772
768773 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
769774 $(RightNavPanel).slideToggle(200, 'swing');
770775 //$(rightNavDrawerIconRightNavDrawerIcon).toggleClass('openIcon closedIcon');
771776 $(RightNavPanel).toggleClass('openDrawer closedDrawer');
772777 }
773778 }
@@ -777,13 +782,15 @@ export function initRossMods() {
777782 if ($(LPanelPin).prop('checked') == true) {
778783 //console.log('adding pin class to Left nav');
779784 $(LeftNavPanel).addClass('pinnedOpen');
785+ $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
780786 } else {
781787 //console.log('removing pin class from Left nav');
782788 $(LeftNavPanel).removeClass('pinnedOpen');
789+ $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
783790
784791 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
785792 $(LeftNavPanel).slideToggle(200, 'swing');
786793 //$(leftNavDrawerIconLeftNavDrawerIcon).toggleClass('openIcon closedIcon');
787794 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
788795 }
789796 }
@@ -794,14 +801,16 @@ export function initRossMods() {
794801 if ($(WIPanelPin).prop('checked') == true) {
795802 console.debug('adding pin class to WI');
796803 $(WorldInfo).addClass('pinnedOpen');
804+ $(WIDrawerIcon).addClass('drawerPinnedOpen');
797805 } else {
798806 console.debug('removing pin class from WI');
799807 $(WorldInfo).removeClass('pinnedOpen');
808+ $(WIDrawerIcon).removeClass('drawerPinnedOpen');
800809
801810 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
802811 console.debug('closing WI after lock removal');
803812 $(WorldInfo).slideToggle(200, 'swing');
804813 //$(WorldInfoDrawerIconWIDrawerIcon).toggleClass('openIcon closedIcon');
805814 $(WorldInfo).toggleClass('openDrawer closedDrawer');
806815 }
807816 }
@@ -812,20 +821,24 @@ export function initRossMods() {
812821 if (LoadLocalBool('NavLockOn') == true) {
813822 //console.log('setting pin class via local var');
814823 $(RightNavPanel).addClass('pinnedOpen');
824+ $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
815825 }
816826 if ($(RPanelPin).prop('checked')) {
817827 console.debug('setting pin class via checkbox state');
818828 $(RightNavPanel).addClass('pinnedOpen');
829+ $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
819830 }
820831 // read the state of left Nav Lock and apply to leftnav classlist
821832 $(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));
822833 if (LoadLocalBool('LNavLockOn') == true) {
823834 //console.log('setting pin class via local var');
824835 $(LeftNavPanel).addClass('pinnedOpen');
836+ $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
825837 }
826838 if ($(LPanelPin).prop('checked')) {
827839 console.debug('setting pin class via checkbox state');
828840 $(LeftNavPanel).addClass('pinnedOpen');
841+ $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
829842 }
830843
831844 // read the state of left Nav Lock and apply to leftnav classlist
@@ -833,11 +846,13 @@ export function initRossMods() {
833846 if (LoadLocalBool('WINavLockOn') == true) {
834847 //console.log('setting pin class via local var');
835848 $(WorldInfo).addClass('pinnedOpen');
849+ $(WIDrawerIcon).addClass('drawerPinnedOpen');
836850 }
837851
838852 if ($(WIPanelPin).prop('checked')) {
839853 console.debug('setting pin class via checkbox state');
840854 $(WorldInfo).addClass('pinnedOpen');
855+ $(WIDrawerIcon).addClass('drawerPinnedOpen');
841856 }
842857
843858 //save state of Right nav being open or closed
@@ -1027,6 +1042,7 @@ export function initRossMods() {
10271042 const editMesDone = $('.mes_edit_done:visible');
10281043 if (editMesDone.length > 0) {
10291044 console.debug('Accepting edits with Ctrl+Enter');
1045+ $('#send_textarea').focus();
10301046 editMesDone.trigger('click');
10311047 return;
10321048 } else if (is_send_press == false) {
public/scripts/openai.js+1 -2
@@ -3931,8 +3931,6 @@ async function onModelChange() {
39313931 $('#openai_max_context').attr('max', max_2mil);
39323932 } else if (value.includes('gemini-1.5-pro')) {
39333933 $('#openai_max_context').attr('max', max_2mil);
3934- } else if (value.match('gemini-1.5-flash-002')) {
3935- $('#openai_max_context').attr('max', max_2mil);
39363934 } else if (value.includes('gemini-1.5-flash')) {
39373935 $('#openai_max_context').attr('max', max_1mil);
39383936 } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
@@ -4584,6 +4582,7 @@ export function isImageInliningSupported() {
45844582 'gemini-1.5-flash-001',
45854583 'gemini-1.5-flash-002',
45864584 'gemini-1.5-flash-exp-0827',
4585+ 'gemini-1.5-flash-8b',
45874586 'gemini-1.5-flash-8b-exp-0827',
45884587 'gemini-1.5-flash-8b-exp-0924',
45894588 'gemini-1.0-pro-vision-latest',
public/scripts/popup.js+5 -0
@@ -1,3 +1,4 @@
1+import dialogPolyfill from '../lib/dialog-polyfill.esm.js';
12import { shouldSendOnEnter } from './RossAscends-mods.js';
23import { power_user } from './power-user.js';
34import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';
@@ -178,6 +179,10 @@ export class Popup {
178179 const template = document.querySelector('#popup_template');
179180 // @ts-ignore
180181 this.dlg = template.content.cloneNode(true).querySelector('.popup');
182+ if (!this.dlg.showModal) {
183+ this.dlg.classList.add('poly_dialog');
184+ dialogPolyfill.registerDialog(this.dlg);
185+ }
181186 this.body = this.dlg.querySelector('.popup-body');
182187 this.content = this.dlg.querySelector('.popup-content');
183188 this.mainInput = this.dlg.querySelector('.popup-input');
src/prompt-converters.js+1 -0
@@ -324,6 +324,7 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
324324 'gemini-1.5-flash-001',
325325 'gemini-1.5-flash-002',
326326 'gemini-1.5-flash-exp-0827',
327+ 'gemini-1.5-flash-8b',
327328 'gemini-1.5-flash-8b-exp-0827',
328329 'gemini-1.5-flash-8b-exp-0924',
329330 'gemini-1.5-pro',