Skip to content

Instantly share code, notes, and snippets.

@LenAnderson
Last active July 31, 2024 22:53
Show Gist options
  • Save LenAnderson/7686604c9da30dee21b76a633a0027f4 to your computer and use it in GitHub Desktop.
Save LenAnderson/7686604c9da30dee21b76a633a0027f4 to your computer and use it in GitHub Desktop.
SillyTavern - CYOA

CYOA

image

Pretty sure it needs the changes in SillyTavern's Parser Followup 2 PR

Extensions

Setup

Make sure replies end on a list of five story suggestions, each of them in <suggestion>...</suggestion>. Use {{getvar::suggestions_1}} numbered 1-5 for instructions.

I will end my response with five distinct single-sentence suggestions for the next story beat, each suggestion surrounded by `<suggestion>...</suggestion>` tags:
- {{getvar::suggestions_1}}
- {{getvar::suggestions_2}}
...

Have an instruction to use <user-context>...</user-context> as guidance for the narration somewhere if you want to use that.

Have a WI entry in an active book with key JB Config with the following content. This will be used to quickly configure the suggestions and user context via Codex. A QR button to open that page in Codex relies on that key.

### User Context [Edit;fa-edit;](#/button/qr/Suggestions/EditUserContext)
{{getvar::suggestions_userContext}}


---


### Suggestions

1. [Edit;fa-edit;](#/button/qr/Suggestions/EditSuggestion/idx=1)
{{getvar::suggestions_1}}
1. [Edit;fa-edit;](#/button/qr/Suggestions/EditSuggestion/idx=2)
{{getvar::suggestions_2}}
1. [Edit;fa-edit;](#/button/qr/Suggestions/EditSuggestion/idx=3)
{{getvar::suggestions_3}}
1. [Edit;fa-edit;](#/button/qr/Suggestions/EditSuggestion/idx=4)
{{getvar::suggestions_4}}
1. [Edit;fa-edit;](#/button/qr/Suggestions/EditSuggestion/idx=5)
{{getvar::suggestions_5}}

Apply the four regex in order.

Import the CSS Snippet to style the suggestions.

Import and activate the QR Set to configure stuff and attach the event listeners (look through each QR and adjust as you feel).

Only now open the chat you want to use this on.

Suggestions

  • Click on the suggestion text to directly send that suggestion and trigger the next gen.
  • Click on the pencil button next to the suggestion text to put the suggestion into the chat input. Edit the text and send as a regular chat response.

QR Buttons

cyoa-buttons

  • Use the lightbulb button to generate a new set of suggestions if you're unhappy with the ones included in the message. The new suggestions can be swiped until satisfied.
  • Use the right-arrows to generate connecting prose between the last two AI messages. The suggested text can be swiped until satisfied, then use the up-arrows to accept (hides the user message and moves the connecting text up between the messages it connects).
  • Use the wrench button to open the config page in Codex.
{
"id": "32aea01c-3e44-4a21-993a-e6a2b402347e",
"scriptName": "<suggestion>",
"findRegex": "/<suggestion>(.+?)<\\/suggestion>/g",
"replaceString": "<div class=\"suggestion\"><button class=\"suggestion\">$1</button><button class=\"edit fa-solid fa-pen-to-square\"><span class=\"text\">$1</span></button></div>",
"trimStrings": [],
"placement": [
1,
2,
3
],
"disabled": false,
"markdownOnly": true,
"promptOnly": false,
"runOnEdit": true,
"substituteRegex": false,
"minDepth": null,
"maxDepth": null
}
{
"id": "4c8c4c78-2a9f-4f2a-bd7e-3d530300d6b4",
"scriptName": "<suggestion> block",
"findRegex": "/<div class=\"suggestion\">.+<\\/div>/s",
"replaceString": "<div class=\"suggestions\">$0</div>",
"trimStrings": [],
"placement": [
1,
2,
3
],
"disabled": false,
"markdownOnly": true,
"promptOnly": false,
"runOnEdit": true,
"substituteRegex": false,
"minDepth": null,
"maxDepth": null
}
{
"id": "999d70b9-72ec-4ce0-92c9-949e9e10191c",
"scriptName": "<suggestion> Hide from Prompt",
"findRegex": "/<suggestion>(.+)<\\/suggestion>/s",
"replaceString": "",
"trimStrings": [],
"placement": [
1,
2,
3
],
"disabled": false,
"markdownOnly": false,
"promptOnly": true,
"runOnEdit": false,
"substituteRegex": false,
"minDepth": null,
"maxDepth": null
}
{
"id": "1e4786a4-a633-4830-92e4-015392db8138",
"scriptName": "<suggestion> Hide in old messages",
"findRegex": "/<div class=\"suggestions\">(.+)<\\/div>/s",
"replaceString": "<details><summary>Suggestions</summary>$0</details>",
"trimStrings": [],
"placement": [
1,
2,
3
],
"disabled": false,
"markdownOnly": true,
"promptOnly": false,
"runOnEdit": true,
"substituteRegex": false,
"minDepth": 1,
"maxDepth": null
}
[
{
"id": "cd005f1c-ea2b-4d3d-a71c-2f6eda621a65",
"name": "<suggestion>",
"isDisabled": false,
"isGlobal": true,
"content": ".mes_text .custom-suggestions {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1em;\n\tmargin: 1em 3em;\n\tpadding: 1em;\n\tborder: 3px solid var(--SmartThemeBorderColor);\n\t&:before {\n\t\tcontent: \"How does the story continue?\";\n\t\tfont-weight: bold;\n\t\tdisplay: block;\n\t\ttext-align: center;\n\t\tfont-size: 1.1em;\n\t}\n}\n.mes_text .custom-suggestions > .custom-suggestion {\n\tdisplay: flex;\n}\n.mes_text .custom-suggestions > .custom-suggestion button {\n color: var(--SmartThemeBodyColor);\n background-color: var(--black50a);\n border: 1px solid var(--SmartThemeBorderColor);\n border-radius: 5px;\n padding: 1em 1em;\n cursor: pointer;\n margin: 5px 0;\n transition: 0.3s;\n align-items: center;\n justify-content: center;\n text-align: center;\n\tfont-size: 1.1em;\n\topacity: 0.5;\n\t&:hover { opacity: 1; }\n}\n.mes_text .custom-suggestions > .custom-suggestion button.custom-suggestion {\n\tflex: 1 1 auto;\n}\n.mes_text .custom-suggestions > .custom-suggestion button.custom-edit {\n\tflex: 0 0 auto;\n\t.custom-text {\n\t\tdisplay: none;\n\t}\n}",
"isCollapsed": true,
"isSynced": true,
"isDeleted": false,
"modifiedOn": 1721753322771,
"themeList": [],
"charList": [],
"groupList": []
},
{
"id": "0c6503c6-8321-448d-94e5-f6415af76cca",
"name": "Details Summary Highlight",
"isDisabled": false,
"isGlobal": true,
"content": "#chat .mes details > summary {\n\tcolor: cyan;\n\tcursor: pointer;\n\tborder-bottom: 2px solid transparent;\n\ttransition: 200ms;\n\t&:hover {\n\t\tborder-bottom-color: cyan;\n\t}\n}",
"isCollapsed": true,
"isSynced": true,
"isDeleted": false,
"modifiedOn": 1720353861855,
"themeList": [],
"charList": [],
"groupList": []
},
{
"id": "540d8231-ca66-4d81-86b3-650b23b38477",
"name": "Collapse Hidden",
"isDisabled": false,
"isGlobal": true,
"content": "#chat .mes[is_system=\"true\"] {\n\theight: 100px !important;\n overflow: clip;\n opacity: 0.25;\n filter: saturate(0);\n\ttransition: 200ms;\n\t&:hover {\n\t\topacity: 1;\n\t\tfilter: saturate(1);\n\t}\n}",
"isCollapsed": true,
"isSynced": false,
"isDeleted": false,
"modifiedOn": 1721756690041,
"themeList": [],
"charList": [],
"groupList": []
}
]
{
"version": 2,
"name": "Suggestions",
"disableSend": false,
"placeBeforeInput": false,
"injectInput": false,
"color": "rgb(48, 196, 196)",
"onlyBorderColor": false,
"qrList": [
{
"id": 4,
"icon": "fa-lightbulb",
"showLabel": true,
"label": "Listener",
"title": "",
"message": "// auto-executed on chat change |\n// register event listeners for click on the suggestion buttons |\n/let hideExtraSuggestions {:\n\t// hide user instruction and AI response if previous message was only suggestions |\n\t/let idx {: /sub {{lastMessageId}} 1 :}() |\n\t/ife {:\n\t\t/and\n\t\t\tleft={:/getmesvar swipe=0 isSuggest:}()\n\t\t\tright={:/getmesvar swipe=0 mes={{var::idx}} isSuggestTrigger:}()\n\t:} |\n\t/then /hide {{var::idx}}-{{lastMessageId}} |\n:} |\n\n\n/fireandforget {:\n\t/delay 1000 |\n\t// remove existing listeners just in case |\n\t/message-off id=suggestionListener |\n\t/message-off id=editListener |\n\t\n\t/message-on event=click quiet=true callback={:\n\t\t/:hideExtraSuggestions |\n\t\t/$ take=textContent {{target}} |\n\t\t/let prompt Continue by weaving the following suggestion into your next response: {{pipe}} |\n\t\t/inputhistory-add {{var::prompt}} |\n\t\t/send {{var::prompt}} |\n\t\t/trigger |\n\t:} button.custom-suggestion |\n\t/setvar key=suggestionListener |\n\t\n\t/message-on event=click quiet=true callback={:\n\t\t/:hideExtraSuggestions |\n\t\t/$ query=.custom-text take=textContent {{target}} |\n\t\t/let prompt Continue by weaving the following suggestion into your next response: {{pipe}} |\n\t\t/setinput {{var::prompt}} |\n\t:} .custom-suggestion button.custom-edit |\n\t/setvar key=editListener |\n:}",
"contextList": [],
"preventAutoExecute": true,
"isHidden": true,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": true,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 6,
"icon": "fa-lightbulb",
"showLabel": false,
"label": "Suggest",
"title": "Generate Suggestions",
"message": "/let prompt Generate five creative suggestions for how the story might continue according to your instructions. Do not write any story text. ONLY output the thinking block to evaluate the current situation and the five suggestions. Nothing else. |\n/send {{var::prompt}} |\n/inputhistory-add {{var::prompt}} |\n/setmesvar key=isSuggestTrigger true |\n/trigger await=true |\n/setmesvar key=isSuggest true |\n",
"contextList": [],
"preventAutoExecute": true,
"isHidden": false,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 8,
"icon": "fa-angle-double-right",
"showLabel": false,
"label": "Connect",
"title": "Generate connection between previous two responses",
"message": "// request connecting prose between last two AI messages |\n// can be swiped until good an then accepted with another QR to move into place |\n/let text Write three sentences to seamlessly segue from your second to last response to the beginning of your last response. Do not continue the story. The three sentence should fit seamlessly between those two responses, without overlapping with either response. ONLY output a thinking block (in which you will think step-by-step about how to best connect the two responses: What is the final story beat of your second to last response? What is the first story beat of your last response? What is missing to fill the gap between those two? Reconsider your answer and think again: What is missing to seamlessly fill the gap without overlap?) followed by the three connecting sentences. Nothing else. |\n/send {{var::text}} |\n/inputhistory-add {{var::text}} |\n/setmesvar key=isSegueTrigger true |\n/trigger await=true |\n/setmesvar key=isSegue true |\n",
"contextList": [],
"preventAutoExecute": true,
"isHidden": false,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 10,
"icon": "fa-angle-double-up",
"showLabel": false,
"label": "Accept Connect",
"title": "Accept current connection request",
"message": "// accept the \"write connection between two responses\" |\n// -> hide user message and move both messages up |\n/let idx {: /sub {{lastMessageId}} 1 :}() |\n/ife {:\n\t/and\n\t\tleft={:/getmesvar swipe=0 isSegue:}()\n\t\tright={:/getmesvar swipe=0 mes={{var::idx}} isSegueTrigger:}()\n:} |\n/then {:\n\t/hide {{var::idx}} |\n\t/message-move from={{var::idx}} up=1 |\n\t/message-move from={{lastMessageId}} up=1 |\n:} |\n/else {:\n\t/echo severity=warning Previous messages are not a connection request. |\n:} |",
"contextList": [],
"preventAutoExecute": true,
"isHidden": false,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 16,
"icon": "fa-wrench",
"showLabel": false,
"label": "Configure Suggestions",
"title": "Configure Suggestions",
"message": "// open the config page in Codex (<user-context> and suggestions) |\n/codex JB Config",
"contextList": [],
"preventAutoExecute": true,
"isHidden": false,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 12,
"showLabel": false,
"label": "SetupConfig",
"title": "",
"message": "/setvar key=suggestions_userContext {: /ifnullish value={{getvar::suggestions_userContext}} tags: talking animals, whimsical, happy ending :}() |\n/setvar key=suggestions_1 {: /ifnullish value={{getvar::suggestions_1}} the first suggestion should ease tension and improve the protagonist's situation (e.g., they get a break, they escape, they find something useful, ...) :}() |\n/setvar key=suggestions_2 {: /ifnullish value={{getvar::suggestions_2}} the second suggestion should create or increase tension and worsen the protagonist's situation (e.g., they get caught or trapped, they get injured, a tool breaks, ...) :}() |\n/setvar key=suggestions_3 {: /ifnullish value={{getvar::suggestions_3}} the third suggestion should lead directly but believably to a wild twist or super weird event :}() |\n/setvar key=suggestions_4 {: /ifnullish value={{getvar::suggestions_4}} the fourth suggestion should slowly move the story forward (i.e., move one tiny step closer to the end of the current scene without ending the current scene) :}() |\n/setvar key=suggestions_5 {: /ifnullish value={{getvar::suggestions_5}} the fifth suggestions should push the story forward (i.e., move one large step closer to the end of the scene, or if feasible end the scene) :}() |",
"contextList": [],
"preventAutoExecute": true,
"isHidden": true,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": true,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 14,
"showLabel": false,
"label": "EditSuggestion",
"title": "",
"message": "// used in Codex config entry to change suggestion texts |\n/let idx {{arg::idx}} |\n/let noIdx |\n/let idxCheck {:\n\t/or\n\t\tleft={:\n\t\t\t/or\n\t\t\t\tleft={: /test left={{var::idx}} rule=eq right= :}()\n\t\t\t\tright={: /test left={{var::idx}} rule=lt right=1 :}()\n\t\t:}()\n\t\tright= {: /test left={{var::idx}} rule=gt right=5 :}()\n\t|\n\t/var noIdx {{pipe}} |\n:} |\n/:idxCheck |\n/while left=noIdx rule=eq right=true {:\n\t/input Which suggestion do you want to change? (1-5) |\n\t/if left={{pipe}} rule=eq right= else={: /return {{pipe}} :} {: /abort | :} |\n\t/var idx {{pipe}} |\n\t/:idxCheck |\n:} |\n/if left=idx rule=eq right= {: /abort | :} |\n/breakpoint |\n/getvar suggestions_{{var::idx}} |\n/input default={{pipe}} Suggestion {{var::idx}} should... |\n/if left={{pipe}} rule=eq right= else={: /return {{pipe}} :} {: /abort | :} |\n/setvar key=suggestions_{{var::idx}} {{pipe}} |\n",
"contextList": [],
"preventAutoExecute": true,
"isHidden": true,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
},
{
"id": 22,
"showLabel": false,
"label": "EditUserContext",
"title": "",
"message": "// used in codex config page to adjust <user-context> |\n/let idx userContext |\n/getvar suggestions_{{var::idx}} |\n/input default={{pipe}} User Context: |\n/if left={{pipe}} rule=eq right= else={: /return {{pipe}} :} {: /abort | :} |\n/setvar key=suggestions_{{var::idx}} {{pipe}} |",
"contextList": [],
"preventAutoExecute": true,
"isHidden": true,
"executeOnStartup": false,
"executeOnUser": false,
"executeOnAi": false,
"executeOnChatChange": false,
"executeOnGroupMemberDraft": false,
"automationId": ""
}
],
"idIndex": 22
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment