synthos 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/bin/synthos.js +3 -0
- package/default-pages/[application].html +87 -0
- package/default-pages/[markdown].html +261 -0
- package/default-pages/[sidebar].html +89 -0
- package/default-pages/[split-application].html +133 -0
- package/default-pages/json_tools.html +176 -0
- package/default-scripts/android.terminal.json +7 -0
- package/default-scripts/linux-terminal.json +7 -0
- package/default-scripts/mac-terminal.json +7 -0
- package/default-scripts/windows-terminal.json +7 -0
- package/dist/files.d.ts +9 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +79 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +9 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +59 -0
- package/dist/init.js.map +1 -0
- package/dist/pages.d.ts +6 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +55 -0
- package/dist/pages.js.map +1 -0
- package/dist/scripts.d.ts +14 -0
- package/dist/scripts.d.ts.map +1 -0
- package/dist/scripts.js +103 -0
- package/dist/scripts.js.map +1 -0
- package/dist/service/createCompletePrompt.d.ts +4 -0
- package/dist/service/createCompletePrompt.d.ts.map +1 -0
- package/dist/service/createCompletePrompt.js +42 -0
- package/dist/service/createCompletePrompt.js.map +1 -0
- package/dist/service/generateImage.d.ts +32 -0
- package/dist/service/generateImage.d.ts.map +1 -0
- package/dist/service/generateImage.js +71 -0
- package/dist/service/generateImage.js.map +1 -0
- package/dist/service/index.d.ts +8 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +24 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/requiresSettings.d.ts +3 -0
- package/dist/service/requiresSettings.d.ts.map +1 -0
- package/dist/service/requiresSettings.js +24 -0
- package/dist/service/requiresSettings.js.map +1 -0
- package/dist/service/server.d.ts +4 -0
- package/dist/service/server.d.ts.map +1 -0
- package/dist/service/server.js +26 -0
- package/dist/service/server.js.map +1 -0
- package/dist/service/transformPage.d.ts +11 -0
- package/dist/service/transformPage.d.ts.map +1 -0
- package/dist/service/transformPage.js +119 -0
- package/dist/service/transformPage.js.map +1 -0
- package/dist/service/useApiRoutes.d.ts +4 -0
- package/dist/service/useApiRoutes.d.ts.map +1 -0
- package/dist/service/useApiRoutes.js +95 -0
- package/dist/service/useApiRoutes.js.map +1 -0
- package/dist/service/useDataRoutes.d.ts +4 -0
- package/dist/service/useDataRoutes.d.ts.map +1 -0
- package/dist/service/useDataRoutes.js +98 -0
- package/dist/service/useDataRoutes.js.map +1 -0
- package/dist/service/usePageRoutes.d.ts +5 -0
- package/dist/service/usePageRoutes.d.ts.map +1 -0
- package/dist/service/usePageRoutes.js +132 -0
- package/dist/service/usePageRoutes.js.map +1 -0
- package/dist/settings.d.ts +13 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +55 -0
- package/dist/settings.js.map +1 -0
- package/dist/synthos-cli.d.ts +2 -0
- package/dist/synthos-cli.d.ts.map +1 -0
- package/dist/synthos-cli.js +43 -0
- package/dist/synthos-cli.js.map +1 -0
- package/images/home.png +0 -0
- package/images/page-management.png +0 -0
- package/images/settings.png +0 -0
- package/images/synthos-square.png +0 -0
- package/package.json +58 -0
- package/required-pages/apis.html +347 -0
- package/required-pages/home.html +83 -0
- package/required-pages/pages.html +135 -0
- package/required-pages/scripts.html +335 -0
- package/required-pages/settings.html +158 -0
- package/src/files.ts +49 -0
- package/src/index.ts +6 -0
- package/src/init.ts +66 -0
- package/src/pages.ts +55 -0
- package/src/scripts.ts +130 -0
- package/src/service/createCompletePrompt.ts +40 -0
- package/src/service/generateImage.ts +101 -0
- package/src/service/index.ts +7 -0
- package/src/service/requiresSettings.ts +22 -0
- package/src/service/server.ts +26 -0
- package/src/service/transformPage.ts +135 -0
- package/src/service/useApiRoutes.ts +95 -0
- package/src/service/useDataRoutes.ts +97 -0
- package/src/service/usePageRoutes.ts +147 -0
- package/src/settings.ts +60 -0
- package/src/synthos-cli.ts +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Steven Ickman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# SynthOS: An AI Powered Interactive Shell Experience
|
|
2
|
+
|
|
3
|
+
[SynthOS](https://github.com/Stevenic/synthos) is an interactive shell experience powered by generative AI, featuring a user interface that is entirely AI-generated. Similar to a wiki, it organizes everything into pages, but with a twist: these pages are self-modifying and can transform into anything. Each page includes a chat panel and a content viewer. Entering a command into the chat panel prompts the configured Large Language Model (LLM) to completely rewrite the current page. The page can morph into anything—from providing answers to questions to becoming a tool that assists you with complex tasks.
|
|
4
|
+
|
|
5
|
+
SynthOS has access to tools in the form of APIs and scripts. Built-in APIs enable SynthOS to read and write objects to local storage or make additional generative AI calls. Scripts are user-defined extensions that allow SynthOS to perform local actions on your machine. You can add scripts that let SynthOS start a build, make a Git commit, or run a cURL command.
|
|
6
|
+
|
|
7
|
+
## Installing SynthOS
|
|
8
|
+
|
|
9
|
+
To get started using SynthOS you'll need to first install a recent version of [Node.js](https://nodejs.org/en/download/package-manager/current). I use version `18.20.2` but any relatively recent version should work. You'll also want to go to OpenAI.com [create a developer account](https://platform.openai.com/login?launch) if you don't have one (it's free.) Once you have your account created you'll need to [generate an API Key](https://platform.openai.com/api-keys) from the Dashboard. Save this key somewhere as you'll need it in a few moments.
|
|
10
|
+
|
|
11
|
+
Open a terminal window and run the following commands:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install --global synthos
|
|
15
|
+
synthos start
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This will install the CLI and start the SynthOS server running. This terminal window needs to stay open to keep the server running. You should see a message saying `SynthOS's server is running on http://localhost:4242`. You can open your browser to that link and you should land on the `settings` page.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Using SynthOS
|
|
22
|
+
|
|
23
|
+
The first time you open SynthOS you're going to be directed to a `settings` page where you can enter your API key:
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
Enter the OpenAI API key you saved into the API Key field and select the version of GPT-4o you'd like to use from the drop down. `gpt-4o-mini` is the cheapest but it's not as creative as `gpt-4o-2024-08-06`. If you're on a trial account then I'd recommend sticking with `gpt-4o-mini` otherwise I'd go with `gpt-4o` for the best creativity. Another strategy would be to stick with `gpt-4o-mini` while using an app but switch to `gpt-4o` when you want to create something new. It's also worth noting, that you only incur LLM costs when chatting with the LLM from the chat window so if you're just using an app it created, there's no cost. And if you're curious the average number of output tokens generated for a simple page is around 2,000 tokens. Add another 500 or so in prompt overhead and most requests will be around 2,500 input tokens and 2,000 generated output tokens.
|
|
28
|
+
|
|
29
|
+
Once you've entered your API key and selected your model press Update and you should be navigated to the `home` page:
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
Type in a query like "create a snake game. make it fill the viewer and include a score" to just verify that all of your settings are properly configured and everything is working.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Page Management
|
|
37
|
+
|
|
38
|
+
A SynthOS server instance is just a collection of named pages. You can think of it almost like a wiki but every page is generated by an LLM with direction from you. If you look directly above the chat input box you'll see controls for performing basic page management:
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
- **Save** - Will save the current page to either a new page or over the top of the current page. Any page can be replaced, even the home page.
|
|
43
|
+
- **Pages** - Navigates you to the `pages` page where you can see a listing of all the current pages. You can navigate to a different page from either the `pages` page or by setting the `/{page_name}` directly in your browsers address bar.
|
|
44
|
+
- **Reset** - Restores the current page back to its last save point. The modifications to a page are cached in the servers memory until their either saved using **Save** or the server is restarted. Clicking **Reset** lets you manually roll back changes and, trust me, you will be clicking this a lot.
|
|
45
|
+
|
|
46
|
+
One thing you'll notice is that there's no **Delete** action. This is actually by design. The LLM has direct access to all of these page management features and I didn't want it randomly deciding to write a program that would delete all of its pages.
|
|
47
|
+
|
|
48
|
+
When you start SynthOS, it creates a `.synthos` subfolder under the folder it was started in. This folder contains your current `settings.json` file and a `.html` file for last save point of every page. Deleting this .html file will delete the page from the collection but you may need to restart the SynthOS server for the changes to take effect.
|
|
49
|
+
|
|
50
|
+
Since all of the pages are just static .html files it means that you're to edit them directly using a tool like [VSCode](https://code.visualstudio.com/). You'll need to click **Reset** in the UI to see your manual changes take effect. Sometimes it's easier to just go in and tweak the final layout of a page manually.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Tips and Tricks
|
|
54
|
+
|
|
55
|
+
I'm new to working with SynthOS as well but here's a couple of things I've already figured out.
|
|
56
|
+
|
|
57
|
+
**Manual Reset**\
|
|
58
|
+
Sometimes the LLM will decide to make a change that breaks the entire page. This is when it's useful to know how to manually reset the page to its last save point. From the address bar append a `/reset` to the current address. So `https://localhost:4242/home/reset` will restore the home page back to its starting state.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
**Save Often**\
|
|
62
|
+
This goes along with the reset tip. Most things you create are going to take a bit of iteration. You're going to ask the LLM to make a change and it's not going to do exactly what you want or worse break the whole page. Anytime you get something you like save it. Saving is cheap and you can have as many drafts as you want. Just use a naming scheme like `my_app_v2`.
|
|
63
|
+
|
|
64
|
+
**Walk the LLM to a Design Step-by-Step**\
|
|
65
|
+
We're asking the LLM to re-generate an entire page on every request. That's asking a lot. You'll get your best results if you have the model focus on one change at a time (start with layout) and then step your way to a final design, saving anytime you get a result you like.
|
|
66
|
+
|
|
67
|
+
**Leverage the [templates]**\
|
|
68
|
+
Use the provided templates as a starting point for structure or better yet, build your own templates! I'm still figuring this out but it seems like you want to start with defining the pages layout first because the LLM will naturally want to make any changes within the confines of the current layout. Once you get your layout the way you want add in visual elements like input boxes and such. Then once you get everything visually the way you want you can add in behavior like calling API's or saving to storage.
|
|
69
|
+
|
|
70
|
+
**Give the LLM Libraries to Use**\
|
|
71
|
+
If you have a specific JavaScript library you want to use, find the link on a CDN or the web and then tell the LLM to use that specific version and it will.
|
package/bin/synthos.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SynthOS - {Application Title}</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #1a1a1a; color: #e0e0e0; height: 100vh; display: flex; }
|
|
10
|
+
.chat-panel { width: 30%; background: #2a2a2a; box-shadow: 0 0 10px rgba(0,0,0,0.3); padding: 20px; display: flex; flex-direction: column; }
|
|
11
|
+
.chat-header { font-size: 24px; padding: 10px; background: #3a7bc8; color: white; text-align: center; border-radius: 10px 10px 0 0; }
|
|
12
|
+
.chat-messages { flex-grow: 1; overflow-y: auto; padding: 15px; margin-top: 10px; background: #333; border-radius: 10px; }
|
|
13
|
+
.chat-message { margin-bottom: 15px; padding: 10px; background: #444; border-radius: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
|
|
14
|
+
.chat-message p { margin-bottom: 5px; line-height: 1.4; }
|
|
15
|
+
.chat-message p strong { font-weight: 600; color: #4a90e2; }
|
|
16
|
+
.chat-message p code { background: #555; padding: 2px 4px; border-radius: 3px; font-family: 'Courier New', Courier, monospace; color: #e0e0e0; }
|
|
17
|
+
.link-group { display: flex; justify-content: space-between; margin: 15px 0; }
|
|
18
|
+
.link-group a { font-size: 14px; color: #4a90e2; text-decoration: none; padding: 5px 10px; border-radius: 5px; transition: background-color 0.3s; }
|
|
19
|
+
.link-group a:hover { background-color: #3a3a3a; }
|
|
20
|
+
form { display: flex; flex-direction: column; width: 100%; }
|
|
21
|
+
.chat-input, .chat-submit { padding: 12px; border: none; border-radius: 25px; width: 100%; font-size: 14px; }
|
|
22
|
+
.chat-input { background: #444; color: #e0e0e0; margin-bottom: 10px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); }
|
|
23
|
+
.chat-submit { background: #3a7bc8; color: white; cursor: pointer; transition: background-color 0.3s; }
|
|
24
|
+
.chat-submit:hover { background: #2a6cb8; }
|
|
25
|
+
.viewer-panel { width: 70%; padding: 20px; background: #2a2a2a; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; box-shadow: -5px 0 10px rgba(0,0,0,0.2); }
|
|
26
|
+
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(26, 26, 26, 0.8); display: none; justify-content: center; align-items: center; z-index: 1000; }
|
|
27
|
+
.spinner { border: 8px solid #333; border-top: 8px solid #3a7bc8; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; }
|
|
28
|
+
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
29
|
+
::-webkit-scrollbar { width: 10px; }
|
|
30
|
+
::-webkit-scrollbar-track { background: #333; }
|
|
31
|
+
::-webkit-scrollbar-thumb { background: #555; border-radius: 5px; }
|
|
32
|
+
::-webkit-scrollbar-thumb:hover { background: #666; }
|
|
33
|
+
.application-title { font-size: 24px; color: white; background: #3a7bc8; padding: 10px; width: 100%; text-align: center; border-radius: 10px 10px 0 0; }
|
|
34
|
+
.application-content { font-size: 18px; color: #ccc; padding: 20px; margin-top: 10px; flex-grow: 1; width: 100%; background: #333; border-radius: 0 0 10px 10px; }
|
|
35
|
+
</style>
|
|
36
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
37
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
38
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<div class="chat-panel">
|
|
42
|
+
<div class="chat-header">SynthOS</div>
|
|
43
|
+
<div class="chat-messages" id="chatMessages">
|
|
44
|
+
<div class="chat-message"><p><strong>SynthOS:</strong> what kind of application would you like?</p></div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="link-group">
|
|
47
|
+
<a href="#" id="saveLink">Save</a>
|
|
48
|
+
<a href="/pages" id="pagesLink">Pages</a>
|
|
49
|
+
<a href="#" id="resetLink">Reset</a>
|
|
50
|
+
</div>
|
|
51
|
+
<form action="/" method="POST" id="chatForm">
|
|
52
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message...">
|
|
53
|
+
<button type="submit" class="chat-submit">Send</button>
|
|
54
|
+
</form>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
57
|
+
<div class="application-title">{Application Title}</div>
|
|
58
|
+
<div class="application-content">{Application Content}</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div id="thoughts" style="display: none;">I've prompted the user for the type of application to create.</div>
|
|
61
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
62
|
+
<script>
|
|
63
|
+
// Basic chat functionality
|
|
64
|
+
document.getElementById("chatInput").focus();
|
|
65
|
+
document.getElementById("chatForm").addEventListener('submit', () => {
|
|
66
|
+
document.getElementById("loadingOverlay").style.display = 'flex';
|
|
67
|
+
document.getElementById("chatForm").action = window.location.pathname;
|
|
68
|
+
});
|
|
69
|
+
document.getElementById("saveLink").addEventListener("click", function() {
|
|
70
|
+
const pageName = prompt("Enter the name of the page to save as:");
|
|
71
|
+
if (pageName) {
|
|
72
|
+
window.location.href = `${window.location.pathname}/save?name=${encodeURIComponent(pageName)}`;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
document.getElementById("resetLink").addEventListener("click", function() {
|
|
76
|
+
window.location.href = `${window.location.pathname}/reset`;
|
|
77
|
+
});
|
|
78
|
+
window.onload = function() {
|
|
79
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
80
|
+
chatMessages.scrollTo({
|
|
81
|
+
top: chatMessages.scrollHeight,
|
|
82
|
+
behavior: 'smooth'
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
</script>
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SynthOS</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #1a1a1a; color: #e0e0e0; height: 100vh; display: flex; }
|
|
10
|
+
.chat-panel { width: 30%; background: #2a2a2a; box-shadow: 0 0 10px rgba(0,0,0,0.3); padding: 20px; display: flex; flex-direction: column; }
|
|
11
|
+
.chat-header { font-size: 24px; padding: 10px; background: #3a7bc8; color: white; text-align: center; border-radius: 10px 10px 0 0; }
|
|
12
|
+
.chat-messages { flex-grow: 1; overflow-y: auto; padding: 15px; margin-top: 10px; background: #333; border-radius: 10px; }
|
|
13
|
+
.chat-message { margin-bottom: 15px; padding: 10px; background: #444; border-radius: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
|
|
14
|
+
.chat-message p { margin-bottom: 5px; line-height: 1.4; }
|
|
15
|
+
.chat-message p strong { font-weight: 600; color: #4a90e2; }
|
|
16
|
+
.chat-message p code { background: #555; padding: 2px 4px; border-radius: 3px; font-family: 'Courier New', Courier, monospace; color: #e0e0e0; }
|
|
17
|
+
.link-group { display: flex; justify-content: space-between; margin: 15px 0; }
|
|
18
|
+
.link-group a { font-size: 14px; color: #4a90e2; text-decoration: none; padding: 5px 10px; border-radius: 5px; transition: background-color 0.3s; }
|
|
19
|
+
.link-group a:hover { background-color: #3a3a3a; }
|
|
20
|
+
form { display: flex; flex-direction: column; width: 100%; }
|
|
21
|
+
.chat-input, .chat-submit { padding: 12px; border: none; border-radius: 25px; width: 100%; font-size: 14px; }
|
|
22
|
+
.chat-input { background: #444; color: #e0e0e0; margin-bottom: 10px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); }
|
|
23
|
+
.chat-submit { background: #3a7bc8; color: white; cursor: pointer; transition: background-color 0.3s; }
|
|
24
|
+
.chat-submit:hover { background: #2a6cb8; }
|
|
25
|
+
.viewer-panel { width: 70%; padding: 20px; background: #2a2a2a; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; box-shadow: -5px 0 10px rgba(0,0,0,0.2); overflow-y: auto; }
|
|
26
|
+
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(26, 26, 26, 0.8); display: none; justify-content: center; align-items: center; z-index: 1000; }
|
|
27
|
+
.spinner { border: 8px solid #333; border-top: 8px solid #3a7bc8; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; }
|
|
28
|
+
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
29
|
+
::-webkit-scrollbar { width: 10px; }
|
|
30
|
+
::-webkit-scrollbar-track { background: #333; }
|
|
31
|
+
::-webkit-scrollbar-thumb { background: #555; border-radius: 5px; }
|
|
32
|
+
::-webkit-scrollbar-thumb:hover { background: #666; }
|
|
33
|
+
.markdown-content { text-align: left; color: #f0f0f0; width: 100%; max-width: 800px; margin: 0 auto; line-height: 1.6; }
|
|
34
|
+
.markdown-content h1, .markdown-content h2, .markdown-content h3, .markdown-content h4, .markdown-content h5 { color: #ffffff; margin-top: 1em; margin-bottom: 0.5em; }
|
|
35
|
+
.markdown-content h1 { font-size: 2.5em; border-bottom: 2px solid #4a90e2; padding-bottom: 0.2em; }
|
|
36
|
+
.markdown-content h2 { font-size: 2em; }
|
|
37
|
+
.markdown-content h3 { font-size: 1.5em; }
|
|
38
|
+
.markdown-content h4 { font-size: 1.2em; }
|
|
39
|
+
.markdown-content h5 { font-size: 1em; }
|
|
40
|
+
.markdown-content p { margin-bottom: 1em; }
|
|
41
|
+
.markdown-content ul, .markdown-content ol { margin-left: 10px; margin-bottom: 1em; }
|
|
42
|
+
.markdown-content li { margin-bottom: 0.5em; }
|
|
43
|
+
.markdown-content blockquote { margin: 1em 0; padding: 0.5em 10px; border-left: 5px solid #4a90e2; background: #333; font-style: italic; }
|
|
44
|
+
.markdown-content code { background: #333; padding: 2px 4px; font-family: 'Courier New', Courier, monospace; }
|
|
45
|
+
.markdown-content pre { background: #333; padding: 15px; border-radius: 4px; overflow-x: auto; margin-bottom: 1em; }
|
|
46
|
+
.markdown-content .code-block { border-radius: 4px; overflow: hidden; margin-bottom: 1em; }
|
|
47
|
+
.markdown-content .code-block pre { border-radius: 0; margin-bottom: 0; }
|
|
48
|
+
.markdown-content a { color: #3498db; text-decoration: none; }
|
|
49
|
+
.markdown-content a:hover { text-decoration: underline; }
|
|
50
|
+
.markdown-content > * + * { margin-top: 1em; }
|
|
51
|
+
.markdown-content table { width: 100%; border-collapse: collapse; margin-bottom: 1em; }
|
|
52
|
+
.markdown-content th, .markdown-content td { padding: 10px; border: 1px solid #444; text-align: left; }
|
|
53
|
+
.markdown-content th { background: #3a3a3a; font-weight: bold; }
|
|
54
|
+
.markdown-content td { background: #2b2b2b; }
|
|
55
|
+
.markdown-content img { max-width: 100%; height: auto; display: block; margin: 1em auto; border-radius: 4px; }
|
|
56
|
+
.code-header { background: #444; padding: 5px 10px; border-radius: 5px 5px 0 0; display: flex; justify-content: space-between; align-items: center; }
|
|
57
|
+
.code-header span { font-size: 0.9em; color: #ccc; }
|
|
58
|
+
.copy-button { background: #5a5a5a; border: none; color: #fff; padding: 5px 10px; border-radius: 5px; cursor: pointer; }
|
|
59
|
+
.copy-button:hover { background: #6a6a6a; }
|
|
60
|
+
</style>
|
|
61
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
62
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
63
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
64
|
+
<script src="https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.10.0/highlight.min.js"></script>
|
|
65
|
+
<link href="https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.10.0/styles/night-owl.min.css" rel="stylesheet">
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div class="chat-panel">
|
|
69
|
+
<div class="chat-header">SynthOS</div>
|
|
70
|
+
<div class="chat-messages" id="chatMessages">
|
|
71
|
+
<div class="chat-message"><p><strong>SynthOS:</strong> what document can I write for you?</p></div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="link-group">
|
|
74
|
+
<a href="#" id="saveLink">Save</a>
|
|
75
|
+
<a href="/pages" id="pagesLink">Pages</a>
|
|
76
|
+
<a href="#" id="resetLink">Reset</a>
|
|
77
|
+
</div>
|
|
78
|
+
<form action="/" method="POST" id="chatForm">
|
|
79
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message...">
|
|
80
|
+
<button type="submit" class="chat-submit">Send</button>
|
|
81
|
+
</form>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
84
|
+
<div class="markdown-content" id="markdownContent">
|
|
85
|
+
<!-- The markdown content will be rendered here -->
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div id="thoughts" style="display: none;">I've prompted the user for the document to write.</div>
|
|
89
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
90
|
+
<script>
|
|
91
|
+
// Basic chat functionality
|
|
92
|
+
document.getElementById("chatInput").focus();
|
|
93
|
+
document.getElementById("chatForm").addEventListener('submit', () => {
|
|
94
|
+
document.getElementById("loadingOverlay").style.display = 'flex';
|
|
95
|
+
document.getElementById("chatForm").action = window.location.pathname;
|
|
96
|
+
});
|
|
97
|
+
document.getElementById("saveLink").addEventListener("click", function() {
|
|
98
|
+
const pageName = prompt("Enter the name of the page to save as:");
|
|
99
|
+
if (pageName) {
|
|
100
|
+
window.location.href = `${window.location.pathname}/save?name=${encodeURIComponent(pageName)}`;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
document.getElementById("resetLink").addEventListener("click", function() {
|
|
104
|
+
window.location.href = `${window.location.pathname}/reset`;
|
|
105
|
+
});
|
|
106
|
+
window.onload = function() {
|
|
107
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
108
|
+
chatMessages.scrollTo({
|
|
109
|
+
top: chatMessages.scrollHeight,
|
|
110
|
+
behavior: 'smooth'
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Render markdown using marked library with custom renderer
|
|
114
|
+
const renderer = new marked.Renderer();
|
|
115
|
+
renderer.code = function(code) {
|
|
116
|
+
const validLanguage = !!(code.lang && hljs.getLanguage(code.lang));
|
|
117
|
+
const highlighted = validLanguage ? hljs.highlight(code.lang, code.text).value : code.text;
|
|
118
|
+
console.log(highlighted);
|
|
119
|
+
return `<div class="code-block">
|
|
120
|
+
<div class="code-header">
|
|
121
|
+
<span>${code.lang || 'plaintext'}</span>
|
|
122
|
+
<button class="copy-button" onclick="copyToClipboard(this)">Copy</button>
|
|
123
|
+
</div>
|
|
124
|
+
<pre><code class="${code.lang}">${highlighted}</code></pre>
|
|
125
|
+
</div>`;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
marked.setOptions({
|
|
129
|
+
renderer: renderer,
|
|
130
|
+
highlight: function(code) {
|
|
131
|
+
const validLanguage = !!(code.lang && hljs.getLanguage(code.lang));
|
|
132
|
+
return validLanguage ? hljs.highlight(code.lang, code.text).value : code.textS;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
document.getElementById('markdownContent').innerHTML = marked.parse(markdownContent);
|
|
137
|
+
|
|
138
|
+
// Initialize mermaid
|
|
139
|
+
mermaid.initialize({ startOnLoad: true, theme: 'dark' });
|
|
140
|
+
mermaid.init(undefined, document.querySelectorAll('.language-mermaid'));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
function copyToClipboard(button) {
|
|
144
|
+
const codeBlock = button.parentElement.nextElementSibling.querySelector('code');
|
|
145
|
+
navigator.clipboard.writeText(codeBlock.textContent).then(() => {
|
|
146
|
+
button.textContent = 'Copied!';
|
|
147
|
+
setTimeout(() => { button.textContent = 'Copy'; }, 2000);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Markdown content to render
|
|
152
|
+
const markdownContent = `# SynthOS's Markdown Showcase
|
|
153
|
+
|
|
154
|
+
This page demonstrates various markdown elements and how they are styled in our custom theme.
|
|
155
|
+
|
|
156
|
+
## Text Formatting
|
|
157
|
+
|
|
158
|
+
You can make text **bold**, *italic*, or ***both***. You can also use ~~strikethrough~~ for deleted text.
|
|
159
|
+
|
|
160
|
+
## Lists
|
|
161
|
+
|
|
162
|
+
### Unordered List
|
|
163
|
+
|
|
164
|
+
- Item 1
|
|
165
|
+
- Item 2
|
|
166
|
+
- Subitem 2.1
|
|
167
|
+
- Subitem 2.2
|
|
168
|
+
- Item 3
|
|
169
|
+
|
|
170
|
+
### Ordered List
|
|
171
|
+
|
|
172
|
+
1. First item
|
|
173
|
+
2. Second item
|
|
174
|
+
3. Third item
|
|
175
|
+
1. Subitem 3.1
|
|
176
|
+
2. Subitem 3.2
|
|
177
|
+
|
|
178
|
+
## Links and Images
|
|
179
|
+
|
|
180
|
+
You can create [links to websites](https://www.example.com) or add images:
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
|
|
184
|
+
## Blockquotes
|
|
185
|
+
|
|
186
|
+
> This is a blockquote. It's great for highlighting important information or quoting sources.
|
|
187
|
+
|
|
188
|
+
## Code Blocks
|
|
189
|
+
|
|
190
|
+
Inline code: \`console.log('Hello, World!');\`
|
|
191
|
+
|
|
192
|
+
Code block:
|
|
193
|
+
|
|
194
|
+
\`\`\`javascript
|
|
195
|
+
function greet(name) {
|
|
196
|
+
return \`Hello, \${name}!\`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(greet('SynthOS'));
|
|
200
|
+
\`\`\`
|
|
201
|
+
|
|
202
|
+
## Tables
|
|
203
|
+
|
|
204
|
+
| Header 1 | Header 2 | Header 3 |
|
|
205
|
+
|----------|----------|----------|
|
|
206
|
+
| Row 1, Col 1 | Row 1, Col 2 | Row 1, Col 3 |
|
|
207
|
+
| Row 2, Col 1 | Row 2, Col 2 | Row 2, Col 3 |
|
|
208
|
+
|
|
209
|
+
## Horizontal Rule
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Task Lists
|
|
214
|
+
|
|
215
|
+
- [x] Complete task
|
|
216
|
+
- [ ] Incomplete task
|
|
217
|
+
- [ ] Another task
|
|
218
|
+
|
|
219
|
+
## Mermaid Diagrams
|
|
220
|
+
|
|
221
|
+
\`\`\`mermaid
|
|
222
|
+
graph TD
|
|
223
|
+
A[Start] --> B{Is it?}
|
|
224
|
+
B -- Yes --> C[OK]
|
|
225
|
+
C --> D[Rethink]
|
|
226
|
+
D --> B
|
|
227
|
+
B -- No ----> E[End]
|
|
228
|
+
\`\`\`
|
|
229
|
+
|
|
230
|
+
This showcase demonstrates the various markdown elements and how they are styled in our custom theme. Feel free to use these examples as a reference when creating your own markdown content!
|
|
231
|
+
|
|
232
|
+
## Additional Content for Scrolling
|
|
233
|
+
|
|
234
|
+
To demonstrate the scrolling functionality, we've added some extra content below.
|
|
235
|
+
|
|
236
|
+
### Lorem Ipsum
|
|
237
|
+
|
|
238
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor, nunc id aliquam tincidunt, nisl nunc tincidunt nunc, vitae aliquam nunc nunc vitae nunc. Sed euismod, nunc id aliquam tincidunt, nisl nunc tincidunt nunc, vitae aliquam nunc nunc vitae nunc.
|
|
239
|
+
|
|
240
|
+
### Code Example
|
|
241
|
+
|
|
242
|
+
Here's another code example to show how syntax highlighting works with scrolling:
|
|
243
|
+
|
|
244
|
+
\`\`\`python
|
|
245
|
+
def fibonacci(n):
|
|
246
|
+
if n <= 1:
|
|
247
|
+
return n
|
|
248
|
+
else:
|
|
249
|
+
return fibonacci(n-1) + fibonacci(n-2)
|
|
250
|
+
|
|
251
|
+
# Print the first 10 Fibonacci numbers
|
|
252
|
+
for i in range(10):
|
|
253
|
+
print(fibonacci(i))
|
|
254
|
+
\`\`\`
|
|
255
|
+
|
|
256
|
+
### Final Thoughts
|
|
257
|
+
|
|
258
|
+
This extended markdown content demonstrates how the viewer panel now scrolls independently of the chat panel, allowing users to read through longer documents while still having access to the chat functionality.`;
|
|
259
|
+
</script>
|
|
260
|
+
</body>
|
|
261
|
+
</html>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SynthOS</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #1a1a1a; color: #e0e0e0; height: 100vh; display: flex; }
|
|
10
|
+
.chat-panel { width: 70%; background: #2a2a2a; box-shadow: 0 0 10px rgba(0,0,0,0.3); padding: 20px; display: flex; flex-direction: column; }
|
|
11
|
+
.chat-header { font-size: 24px; padding: 10px; background: #3a7bc8; color: white; text-align: center; border-radius: 10px 10px 0 0; }
|
|
12
|
+
.chat-messages { flex-grow: 1; overflow-y: auto; padding: 15px; margin-top: 10px; background: #333; border-radius: 10px; }
|
|
13
|
+
.chat-message { margin-bottom: 15px; padding: 10px; background: #444; border-radius: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
|
|
14
|
+
.chat-message p { margin-bottom: 5px; line-height: 1.4; }
|
|
15
|
+
.chat-message p strong { font-weight: 600; color: #4a90e2; }
|
|
16
|
+
.chat-message p code { background: #555; padding: 2px 4px; border-radius: 3px; font-family: 'Courier New', Courier, monospace; color: #e0e0e0; }
|
|
17
|
+
.link-group { display: flex; justify-content: space-between; margin: 15px 0; }
|
|
18
|
+
.link-group a { font-size: 14px; color: #4a90e2; text-decoration: none; padding: 5px 10px; border-radius: 5px; transition: background-color 0.3s; }
|
|
19
|
+
.link-group a:hover { background-color: #3a3a3a; }
|
|
20
|
+
form { display: flex; flex-direction: column; width: 100%; }
|
|
21
|
+
.chat-input, .chat-submit { padding: 12px; border: none; border-radius: 25px; width: 100%; font-size: 14px; }
|
|
22
|
+
.chat-input { background: #444; color: #e0e0e0; margin-bottom: 10px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); }
|
|
23
|
+
.chat-submit { background: #3a7bc8; color: white; cursor: pointer; transition: background-color 0.3s; }
|
|
24
|
+
.chat-submit:hover { background: #2a6cb8; }
|
|
25
|
+
.sidebar-panel { width: 30%; background: #2a2a2a; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; box-shadow: -5px 0 10px rgba(0,0,0,0.2); }
|
|
26
|
+
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(26, 26, 26, 0.8); display: none; justify-content: center; align-items: center; z-index: 1000; }
|
|
27
|
+
.spinner { border: 8px solid #333; border-top: 8px solid #3a7bc8; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; }
|
|
28
|
+
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
|
29
|
+
::-webkit-scrollbar { width: 10px; }
|
|
30
|
+
::-webkit-scrollbar-track { background: #333; }
|
|
31
|
+
::-webkit-scrollbar-thumb { background: #555; border-radius: 5px; }
|
|
32
|
+
::-webkit-scrollbar-thumb:hover { background: #666; }
|
|
33
|
+
.sidebar-title { font-size: 24px; color: #f0f0f0; background: #3a3a3a; padding: 10px; border-bottom: 1px solid #444; width: 100%; text-align: center; }
|
|
34
|
+
.sidebar-content { font-size: 18px; color: #ccc; padding: 10px; margin-top:10px; flex-grow: 1; width: 100%; overflow-y: auto; }
|
|
35
|
+
</style>
|
|
36
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
37
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
38
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<div class="chat-panel">
|
|
42
|
+
<div class="chat-header">SynthOS</div>
|
|
43
|
+
<div class="chat-messages" id="chatMessages">
|
|
44
|
+
<div class="chat-message"><p><strong>SynthOS:</strong> what would you like in the sidebar?</p></div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="link-group">
|
|
47
|
+
<a href="#" id="saveLink">Save</a>
|
|
48
|
+
<a href="/pages" id="pagesLink">Pages</a>
|
|
49
|
+
<a href="#" id="resetLink">Reset</a>
|
|
50
|
+
</div>
|
|
51
|
+
<form action="/" method="POST" id="chatForm">
|
|
52
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message...">
|
|
53
|
+
<button type="submit" class="chat-submit">Send</button>
|
|
54
|
+
</form>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="sidebar-panel" id="sidebarPanel">
|
|
57
|
+
<div class="sidebar-title">Sidebar</div>
|
|
58
|
+
<div class="sidebar-content">
|
|
59
|
+
<p>This is the sidebar content. You can customize this area with various widgets, information, or controls as needed.</p>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div id="thoughts" style="display: none;">I've prompted the user for what they would like to add to teh sidebar.</div>
|
|
63
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
64
|
+
<script>
|
|
65
|
+
// Basic chat functionality
|
|
66
|
+
document.getElementById("chatInput").focus();
|
|
67
|
+
document.getElementById("chatForm").addEventListener('submit', () => {
|
|
68
|
+
document.getElementById("loadingOverlay").style.display = 'flex';
|
|
69
|
+
document.getElementById("chatForm").action = window.location.pathname;
|
|
70
|
+
});
|
|
71
|
+
document.getElementById("saveLink").addEventListener("click", function() {
|
|
72
|
+
const pageName = prompt("Enter the name of the page to save as:");
|
|
73
|
+
if (pageName) {
|
|
74
|
+
window.location.href = `${window.location.pathname}/save?name=${encodeURIComponent(pageName)}`;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
document.getElementById("resetLink").addEventListener("click", function() {
|
|
78
|
+
window.location.href = `${window.location.pathname}/reset`;
|
|
79
|
+
});
|
|
80
|
+
window.onload = function() {
|
|
81
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
82
|
+
chatMessages.scrollTo({
|
|
83
|
+
top: chatMessages.scrollHeight,
|
|
84
|
+
behavior: 'smooth'
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
</script>
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|