Merge branch 'staging' into tool-calling

689dbad2d16a0d50df2146b535b3c067277a35b1

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

14 files changed, +1316 -12Ignore whitespace
.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
22SillyTavern 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
24Wir 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
28SillyTavern (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
30Angefangen 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
341. 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!
352. Wir bieten weder Online- oder gehosteten Dienste an, noch verfolgen wir programmgesteuert Benutzerdaten.
363. 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
40SillyTavern 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
45Wenn 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
49Da 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
53Die 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
69Oder 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
771. Sende Pull-Requests. Lerne, wie du beitragen kannst: [CONTRIBUTING.md](../CONTRIBUTING.md)
782. Sende Feature Requests und Issues unter Verwendung der bereitgestellten Vorlagen.
793. 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
88SillyTavern 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
90Das 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
92Um 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
94Um 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
108SillyTavern 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
118Tutorials 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
1321. Installiere [NodeJS](https://nodejs.org/en) (die neueste LTS-Version wird empfohlen).
1332. Installiere [Git für Windows](https://gitforwindows.org/).
1343. Öffne den Windows-Explorer (`Win+E`).
1354. Gehe zu oder erstelle einen Ordner, der nicht von Windows kontrolliert oder überwacht wird. (z.B.: C:\MySpecialFolder\)
1365. Öffne ein Eingabeaufforderungsfenster in diesem Ordner, indem du in die 'Adressleiste' oben klickst, `cmd` eingibst und Enter drückst.
1376. 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
1427. Sobald alles geklont ist, doppelklicke auf `Start.bat`, damit NodeJS seine Anforderungen installiert.
1438. 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
1491. Installiere [NodeJS](https://nodejs.org/en) (die neueste LTS-Version wird empfohlen).
1502. Installiere [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32).
1513. 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).
1524. 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.
1535. 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`.
1546. 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.)
1557. Nach dem Doppelklicken sollte ein großes schwarzes Konsolenfenster erscheinen, und SillyTavern beginnt, das zu installieren, was es zum Betrieb benötigt.
1568. Nach dem Installationsprozess sollte das Konsolenfenster so aussehen, und ein SillyTavern-Tab sollte in deinem Browser geöffnet sein.
1579. Verbinde dich mit einer der [unterstützten APIs](https://docs.sillytavern.app/usage/api-connections/) und beginne zu chatten!
158
159## 🐧 Linux & 🍎 MacOS
160
161Für MacOS/Linux werden all diese Schritte in einem Terminal durchgeführt.
162
1631. Installiere git und nodeJS (die Methode zur Durchführung hängt von deinem Betriebssystem ab).
1642. 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
1693. `cd SillyTavern`, um in den Installationsordner zu navigieren.
1704. 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
177Der 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
1811. 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
184cmd /c winget install -e --id Git.Git
185```
186
1872. 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
190git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
191```
192
193### Für Linux-Nutzer
194
1951. Öffne dein bevorzugtes Terminal und installiere git.
1962. Klone den SillyTavern-Launcher mit:
197
198```shell
199git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
200```
201
2023. Starte die installer.sh mit:
203
204```shell
205chmod +x install.sh && ./install.sh
206```
207
2084. Nach der Installation starte die launcher.sh mit:
209
210```shell
211chmod +x launcher.sh && ./launcher.sh
212```
213
214### Für Mac-Nutzer
215
2161. Ö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
2222. Installiere git mit:
223
224```shell
225brew install git
226```
227
2283. Klone den SillyTavern-Launcher mit:
229
230```shell
231git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
232```
233
2344. Starte die installer.sh mit:
235
236```shell
237chmod +x install.sh && ./install.sh
238```
239
2405. Nach der Installation starte die launcher.sh mit:
241
242```shell
243chmod +x launcher.sh && ./launcher.sh
244```
245
246## 🐋 Installation über Docker
247
248Diese 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
252Wir 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
256Du 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
2771. Öffne deine Befehlszeile.
2782. 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
297SillyTavern speichert deine API-Schlüssel in einer Datei `secrets.json` im Benutzerdatenverzeichnis (`/data/default-user/secrets.json` ist der Standardpfad).
298
299Standardmäßig sind API-Schlüssel nach dem Speichern und Aktualisieren der Seite nicht mehr über die Benutzeroberfläche sichtbar.
300
301Um die Ansicht deiner Schlüssel zu aktivieren:
302
3031. Setze den Wert von `allowKeysExposure` auf `true` in der Datei `config.yaml`.
3042. Starte den SillyTavern-Server neu.
3053. Klicke auf den Link 'Verborgene API-Schlüssel anzeigen' in der unteren rechten Ecke des API-Verbindungsfeldes.
306
307## Befehlszeilenargumente
308
309Sie können Befehlszeilenargumente an den Start des SillyTavern-Servers übergeben, um einige Einstellungen in `config.yaml` zu überschreiben.
310
311### Beispiele
312
313```shell
314node server.js --port 8000 --listen false
315# oder
316npm run start -- --port 8000 --listen false
317# oder (nur Windows)
318Start.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
349Dies 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
351Lies die ausführliche Anleitung zum Einrichten von Remoteverbindungen in den [Docs](https://docs.sillytavern.app/usage/remoteconnections/).
352
353Mö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
3571. Deaktiviere den Unschärfeeffekt und aktiviere "Verringerte Bewegung" im Bedienfeld "Benutzereinstellungen" (UI-Design schaltet Kategorie um).
3582. Wenn du Response Streaming verwendest, stelle die Streaming-FPS auf einen niedrigeren Wert ein (10-15 FPS werden empfohlen).
3593. 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,
364aber OHNE JEGLICHE GARANTIE; nicht einmal die stillschweigende Garantie der
365MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Siehe die
366GNU 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
6<div align="center">6<div align="center">
77
8[English](readme.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md)8[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md)
99
10[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)10[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
11[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)11[![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
8<div align="center">8<div align="center">
99
10[English](readme.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский10[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский
1111
12[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)12[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
13[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)13[![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
6<div align="center">6<div align="center">
77
8[English](readme.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)8[English](readme.md) | [German](readme-de_de.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
99
10[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)10[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
11[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)11[![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
5<div align="center">5<div align="center">
66
7English | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)7English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
88
9[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)9[![GitHub Stars](https://img.shields.io/github/stars/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/stargazers)
10[![GitHub Forks](https://img.shields.io/github/forks/SillyTavern/SillyTavern.svg)](https://github.com/SillyTavern/SillyTavern/network)10[![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');
1@import url('./popup-safari-fix.css');2@import url('./popup-safari-fix.css');
23
3dialog {4dialog {
@@ -42,7 +43,7 @@ dialog {
42 display: flex;43 display: flex;
43 flex-direction: column;44 flex-direction: column;
44 overflow: hidden;45 overflow: hidden;
45 width: 100%;46 width: min(100%, 100vw);
46 height: 100%;47 height: 100%;
47 padding: 1px;48 padding: 1px;
48}49}
public/index.html+1 -0
@@ -2888,6 +2888,7 @@
2888 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>2888 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
2889 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>2889 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
2890 <option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>2890 <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>
2891 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>2892 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
2892 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>2893 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
2893 <option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option>2894 <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}
\ No newline at end of file37 \ 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_.
2var supportCustomEvent = window.CustomEvent;
3if (!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 */
21function 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 */
33function 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 */
62function 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 */
85function 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 */
101function 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 */
114function 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 */
125function 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 */
158function isConnected(element) {
159 return element.isConnected || document.body.contains(element);
160}
161
162/**
163 * @param {!Event} event
164 * @return {?Element}
165 */
166function 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 */
192function 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 */
231function 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
290dialogPolyfillInfo.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
473var dialogPolyfill = {};
474
475dialogPolyfill.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
481dialogPolyfill.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
510dialogPolyfill.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 */
530dialogPolyfill.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 */
544dialogPolyfill.registerDialog = function(element) {
545 if (!element.showModal) {
546 dialogPolyfill.forceRegisterDialog(element);
547 }
548};
549
550/**
551 * @constructor
552 */
553dialogPolyfill.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 */
602dialogPolyfill.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 */
612dialogPolyfill.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 */
621dialogPolyfill.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 */
645dialogPolyfill.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
657dialogPolyfill.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
686dialogPolyfill.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 */
710dialogPolyfill.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 */
728dialogPolyfill.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 */
743dialogPolyfill.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
754dialogPolyfill.dm = new dialogPolyfill.DialogManager();
755dialogPolyfill.formSubmitter = null;
756dialogPolyfill.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 */
762if (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
858export default dialogPolyfill;
public/script.js+5 -2
@@ -1884,6 +1884,7 @@ export async function reloadCurrentChat() {
1884export async function sendTextareaMessage() {1884export async function sendTextareaMessage() {
1885 if (is_send_press) return;1885 if (is_send_press) return;
1886 if (isExecutingCommandsFromChatInput) return;1886 if (isExecutingCommandsFromChatInput) return;
1887 if (this_edit_mes_id) return; // don't proceed if editing a message
18871888
1888 let generateType;1889 let generateType;
1889 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last1890 // "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 () {
10054 }10055 }
1005510056
10056 else if (id == 'option_continue') {10057 else if (id == 'option_continue') {
10058 if (this_edit_mes_id) return; // don't proceed if editing a message
10059
10057 if (is_send_press == false || fromSlashCommand) {10060 if (is_send_press == false || fromSlashCommand) {
10058 is_send_press = true;10061 is_send_press = true;
10059 Generate('continue', buildOrFillAdditionalArgs());10062 Generate('continue', buildOrFillAdditionalArgs());
@@ -10720,7 +10723,7 @@ jQuery(async function () {
10720 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {10723 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
10721 await delay(50); $(this).closest('.drawer-content').removeClass('resizing');10724 await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
10722 });10725 });
10723 $('.openIcon').toggleClass('closedIcon openIcon');10726 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
10724 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');10727 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
10725 icon.toggleClass('openIcon closedIcon');10728 icon.toggleClass('openIcon closedIcon');
10726 drawer.toggleClass('openDrawer closedDrawer');10729 drawer.toggleClass('openDrawer closedDrawer');
@@ -10806,7 +10809,7 @@ jQuery(async function () {
10806 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', function () {10809 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', function () {
10807 $(this).closest('.drawer-content').removeClass('resizing');10810 $(this).closest('.drawer-content').removeClass('resizing');
10808 });10811 });
10809 $('.openIcon').toggleClass('closedIcon openIcon');10812 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
10810 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');10813 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
1081110814
10812 }10815 }
public/scripts/RossAscends-mods.js+19 -3
@@ -45,8 +45,11 @@ var LPanelPin = document.getElementById('lm_button_panel_pin');
45var WIPanelPin = document.getElementById('WI_panel_pin');45var WIPanelPin = document.getElementById('WI_panel_pin');
4646
47var RightNavPanel = document.getElementById('right-nav-panel');47var RightNavPanel = document.getElementById('right-nav-panel');
48var RightNavDrawerIcon = document.getElementById('rightNavDrawerIcon');
48var LeftNavPanel = document.getElementById('left-nav-panel');49var LeftNavPanel = document.getElementById('left-nav-panel');
50var LeftNavDrawerIcon = document.getElementById('leftNavDrawerIcon');
49var WorldInfo = document.getElementById('WorldInfo');51var WorldInfo = document.getElementById('WorldInfo');
52var WIDrawerIcon = document.getElementById('WIDrawerIcon');
5053
51var SelectedCharacterTab = document.getElementById('rm_button_selected_ch');54var SelectedCharacterTab = document.getElementById('rm_button_selected_ch');
5255
@@ -761,13 +764,15 @@ export function initRossMods() {
761 if ($(RPanelPin).prop('checked') == true) {764 if ($(RPanelPin).prop('checked') == true) {
762 //console.log('adding pin class to right nav');765 //console.log('adding pin class to right nav');
763 $(RightNavPanel).addClass('pinnedOpen');766 $(RightNavPanel).addClass('pinnedOpen');
767 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
764 } else {768 } else {
765 //console.log('removing pin class from right nav');769 //console.log('removing pin class from right nav');
766 $(RightNavPanel).removeClass('pinnedOpen');770 $(RightNavPanel).removeClass('pinnedOpen');
771 $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
767772
768 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {773 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
769 $(RightNavPanel).slideToggle(200, 'swing');774 $(RightNavPanel).slideToggle(200, 'swing');
770 //$(rightNavDrawerIcon).toggleClass('openIcon closedIcon');775 $(RightNavDrawerIcon).toggleClass('openIcon closedIcon');
771 $(RightNavPanel).toggleClass('openDrawer closedDrawer');776 $(RightNavPanel).toggleClass('openDrawer closedDrawer');
772 }777 }
773 }778 }
@@ -777,13 +782,15 @@ export function initRossMods() {
777 if ($(LPanelPin).prop('checked') == true) {782 if ($(LPanelPin).prop('checked') == true) {
778 //console.log('adding pin class to Left nav');783 //console.log('adding pin class to Left nav');
779 $(LeftNavPanel).addClass('pinnedOpen');784 $(LeftNavPanel).addClass('pinnedOpen');
785 $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
780 } else {786 } else {
781 //console.log('removing pin class from Left nav');787 //console.log('removing pin class from Left nav');
782 $(LeftNavPanel).removeClass('pinnedOpen');788 $(LeftNavPanel).removeClass('pinnedOpen');
789 $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
783790
784 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {791 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
785 $(LeftNavPanel).slideToggle(200, 'swing');792 $(LeftNavPanel).slideToggle(200, 'swing');
786 //$(leftNavDrawerIcon).toggleClass('openIcon closedIcon');793 $(LeftNavDrawerIcon).toggleClass('openIcon closedIcon');
787 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');794 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
788 }795 }
789 }796 }
@@ -794,14 +801,16 @@ export function initRossMods() {
794 if ($(WIPanelPin).prop('checked') == true) {801 if ($(WIPanelPin).prop('checked') == true) {
795 console.debug('adding pin class to WI');802 console.debug('adding pin class to WI');
796 $(WorldInfo).addClass('pinnedOpen');803 $(WorldInfo).addClass('pinnedOpen');
804 $(WIDrawerIcon).addClass('drawerPinnedOpen');
797 } else {805 } else {
798 console.debug('removing pin class from WI');806 console.debug('removing pin class from WI');
799 $(WorldInfo).removeClass('pinnedOpen');807 $(WorldInfo).removeClass('pinnedOpen');
808 $(WIDrawerIcon).removeClass('drawerPinnedOpen');
800809
801 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {810 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
802 console.debug('closing WI after lock removal');811 console.debug('closing WI after lock removal');
803 $(WorldInfo).slideToggle(200, 'swing');812 $(WorldInfo).slideToggle(200, 'swing');
804 //$(WorldInfoDrawerIcon).toggleClass('openIcon closedIcon');813 $(WIDrawerIcon).toggleClass('openIcon closedIcon');
805 $(WorldInfo).toggleClass('openDrawer closedDrawer');814 $(WorldInfo).toggleClass('openDrawer closedDrawer');
806 }815 }
807 }816 }
@@ -812,20 +821,24 @@ export function initRossMods() {
812 if (LoadLocalBool('NavLockOn') == true) {821 if (LoadLocalBool('NavLockOn') == true) {
813 //console.log('setting pin class via local var');822 //console.log('setting pin class via local var');
814 $(RightNavPanel).addClass('pinnedOpen');823 $(RightNavPanel).addClass('pinnedOpen');
824 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
815 }825 }
816 if ($(RPanelPin).prop('checked')) {826 if ($(RPanelPin).prop('checked')) {
817 console.debug('setting pin class via checkbox state');827 console.debug('setting pin class via checkbox state');
818 $(RightNavPanel).addClass('pinnedOpen');828 $(RightNavPanel).addClass('pinnedOpen');
829 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
819 }830 }
820 // read the state of left Nav Lock and apply to leftnav classlist831 // read the state of left Nav Lock and apply to leftnav classlist
821 $(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));832 $(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));
822 if (LoadLocalBool('LNavLockOn') == true) {833 if (LoadLocalBool('LNavLockOn') == true) {
823 //console.log('setting pin class via local var');834 //console.log('setting pin class via local var');
824 $(LeftNavPanel).addClass('pinnedOpen');835 $(LeftNavPanel).addClass('pinnedOpen');
836 $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
825 }837 }
826 if ($(LPanelPin).prop('checked')) {838 if ($(LPanelPin).prop('checked')) {
827 console.debug('setting pin class via checkbox state');839 console.debug('setting pin class via checkbox state');
828 $(LeftNavPanel).addClass('pinnedOpen');840 $(LeftNavPanel).addClass('pinnedOpen');
841 $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
829 }842 }
830843
831 // read the state of left Nav Lock and apply to leftnav classlist844 // read the state of left Nav Lock and apply to leftnav classlist
@@ -833,11 +846,13 @@ export function initRossMods() {
833 if (LoadLocalBool('WINavLockOn') == true) {846 if (LoadLocalBool('WINavLockOn') == true) {
834 //console.log('setting pin class via local var');847 //console.log('setting pin class via local var');
835 $(WorldInfo).addClass('pinnedOpen');848 $(WorldInfo).addClass('pinnedOpen');
849 $(WIDrawerIcon).addClass('drawerPinnedOpen');
836 }850 }
837851
838 if ($(WIPanelPin).prop('checked')) {852 if ($(WIPanelPin).prop('checked')) {
839 console.debug('setting pin class via checkbox state');853 console.debug('setting pin class via checkbox state');
840 $(WorldInfo).addClass('pinnedOpen');854 $(WorldInfo).addClass('pinnedOpen');
855 $(WIDrawerIcon).addClass('drawerPinnedOpen');
841 }856 }
842857
843 //save state of Right nav being open or closed858 //save state of Right nav being open or closed
@@ -1027,6 +1042,7 @@ export function initRossMods() {
1027 const editMesDone = $('.mes_edit_done:visible');1042 const editMesDone = $('.mes_edit_done:visible');
1028 if (editMesDone.length > 0) {1043 if (editMesDone.length > 0) {
1029 console.debug('Accepting edits with Ctrl+Enter');1044 console.debug('Accepting edits with Ctrl+Enter');
1045 $('#send_textarea').focus();
1030 editMesDone.trigger('click');1046 editMesDone.trigger('click');
1031 return;1047 return;
1032 } else if (is_send_press == false) {1048 } else if (is_send_press == false) {
public/scripts/openai.js+1 -2
@@ -3931,8 +3931,6 @@ async function onModelChange() {
3931 $('#openai_max_context').attr('max', max_2mil);3931 $('#openai_max_context').attr('max', max_2mil);
3932 } else if (value.includes('gemini-1.5-pro')) {3932 } else if (value.includes('gemini-1.5-pro')) {
3933 $('#openai_max_context').attr('max', max_2mil);3933 $('#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);
3936 } else if (value.includes('gemini-1.5-flash')) {3934 } else if (value.includes('gemini-1.5-flash')) {
3937 $('#openai_max_context').attr('max', max_1mil);3935 $('#openai_max_context').attr('max', max_1mil);
3938 } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {3936 } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
@@ -4584,6 +4582,7 @@ export function isImageInliningSupported() {
4584 'gemini-1.5-flash-001',4582 'gemini-1.5-flash-001',
4585 'gemini-1.5-flash-002',4583 'gemini-1.5-flash-002',
4586 'gemini-1.5-flash-exp-0827',4584 'gemini-1.5-flash-exp-0827',
4585 'gemini-1.5-flash-8b',
4587 'gemini-1.5-flash-8b-exp-0827',4586 'gemini-1.5-flash-8b-exp-0827',
4588 'gemini-1.5-flash-8b-exp-0924',4587 'gemini-1.5-flash-8b-exp-0924',
4589 'gemini-1.0-pro-vision-latest',4588 'gemini-1.0-pro-vision-latest',
public/scripts/popup.js+5 -0
@@ -1,3 +1,4 @@
1import dialogPolyfill from '../lib/dialog-polyfill.esm.js';
1import { shouldSendOnEnter } from './RossAscends-mods.js';2import { shouldSendOnEnter } from './RossAscends-mods.js';
2import { power_user } from './power-user.js';3import { power_user } from './power-user.js';
3import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';4import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';
@@ -178,6 +179,10 @@ export class Popup {
178 const template = document.querySelector('#popup_template');179 const template = document.querySelector('#popup_template');
179 // @ts-ignore180 // @ts-ignore
180 this.dlg = template.content.cloneNode(true).querySelector('.popup');181 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 }
181 this.body = this.dlg.querySelector('.popup-body');186 this.body = this.dlg.querySelector('.popup-body');
182 this.content = this.dlg.querySelector('.popup-content');187 this.content = this.dlg.querySelector('.popup-content');
183 this.mainInput = this.dlg.querySelector('.popup-input');188 this.mainInput = this.dlg.querySelector('.popup-input');
src/prompt-converters.js+1 -0
@@ -324,6 +324,7 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
324 'gemini-1.5-flash-001',324 'gemini-1.5-flash-001',
325 'gemini-1.5-flash-002',325 'gemini-1.5-flash-002',
326 'gemini-1.5-flash-exp-0827',326 'gemini-1.5-flash-exp-0827',
327 'gemini-1.5-flash-8b',
327 'gemini-1.5-flash-8b-exp-0827',328 'gemini-1.5-flash-8b-exp-0827',
328 'gemini-1.5-flash-8b-exp-0924',329 'gemini-1.5-flash-8b-exp-0924',
329 'gemini-1.5-pro',330 'gemini-1.5-pro',