synthos 0.6.0 → 0.7.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/README.md +33 -1
- package/default-pages/app_builder.html +40 -0
- package/default-pages/app_builder.json +1 -0
- package/default-pages/json_tools.html +89 -159
- package/default-pages/json_tools.json +1 -0
- package/default-pages/my_notes.html +33 -0
- package/default-pages/my_notes.json +12 -0
- package/default-pages/neon_asteroids.html +77 -0
- package/default-pages/neon_asteroids.json +12 -0
- package/default-pages/sidebar_builder.html +49 -0
- package/default-pages/sidebar_builder.json +1 -0
- package/default-pages/solar_explorer.html +1956 -0
- package/default-pages/solar_explorer.json +12 -0
- package/default-pages/solar_tutorial.html +476 -0
- package/default-pages/solar_tutorial.json +1 -0
- package/default-pages/two-panel_builder.html +66 -0
- package/default-pages/two-panel_builder.json +1 -0
- package/dist/connectors/index.d.ts +3 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +6 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/registry.d.ts +3 -0
- package/dist/connectors/registry.d.ts.map +1 -0
- package/dist/connectors/registry.js +100 -0
- package/dist/connectors/registry.js.map +1 -0
- package/dist/connectors/types.d.ts +61 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +3 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +12 -1
- package/dist/files.js.map +1 -1
- package/dist/init.d.ts +8 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +155 -3
- package/dist/init.js.map +1 -1
- package/dist/migrations.d.ts +11 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +281 -0
- package/dist/migrations.js.map +1 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +10 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/providers.d.ts +7 -0
- package/dist/models/providers.d.ts.map +1 -0
- package/dist/models/providers.js +33 -0
- package/dist/models/providers.js.map +1 -0
- package/dist/models/types.d.ts +21 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +3 -0
- package/dist/models/types.js.map +1 -0
- package/dist/pages.d.ts +21 -2
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +202 -23
- package/dist/pages.js.map +1 -1
- package/dist/scripts.js +2 -2
- package/dist/scripts.js.map +1 -1
- package/dist/service/createCompletePrompt.d.ts +3 -2
- package/dist/service/createCompletePrompt.d.ts.map +1 -1
- package/dist/service/createCompletePrompt.js +11 -16
- package/dist/service/createCompletePrompt.js.map +1 -1
- package/dist/service/debugLog.d.ts +11 -0
- package/dist/service/debugLog.d.ts.map +1 -0
- package/dist/service/debugLog.js +26 -0
- package/dist/service/debugLog.js.map +1 -0
- package/dist/service/modelInstructions.d.ts +7 -0
- package/dist/service/modelInstructions.d.ts.map +1 -0
- package/dist/service/modelInstructions.js +16 -0
- package/dist/service/modelInstructions.js.map +1 -0
- package/dist/service/requiresSettings.d.ts +2 -2
- package/dist/service/requiresSettings.d.ts.map +1 -1
- package/dist/service/requiresSettings.js.map +1 -1
- package/dist/service/server.d.ts.map +1 -1
- package/dist/service/server.js +15 -0
- package/dist/service/server.js.map +1 -1
- package/dist/service/transformPage.d.ts +81 -2
- package/dist/service/transformPage.d.ts.map +1 -1
- package/dist/service/transformPage.js +672 -82
- package/dist/service/transformPage.js.map +1 -1
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +579 -13
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.d.ts +4 -0
- package/dist/service/useConnectorRoutes.d.ts.map +1 -0
- package/dist/service/useConnectorRoutes.js +389 -0
- package/dist/service/useConnectorRoutes.js.map +1 -0
- package/dist/service/useDataRoutes.d.ts.map +1 -1
- package/dist/service/useDataRoutes.js +83 -70
- package/dist/service/useDataRoutes.js.map +1 -1
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +243 -38
- package/dist/service/usePageRoutes.js.map +1 -1
- package/dist/settings.d.ts +33 -4
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +108 -15
- package/dist/settings.js.map +1 -1
- package/dist/synthos-cli.d.ts.map +1 -1
- package/dist/synthos-cli.js +11 -1
- package/dist/synthos-cli.js.map +1 -1
- package/dist/themes.d.ts +9 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +64 -0
- package/dist/themes.js.map +1 -0
- package/package.json +5 -3
- package/required-pages/builder.html +74 -0
- package/required-pages/builder.json +1 -0
- package/required-pages/pages.html +169 -126
- package/required-pages/pages.json +1 -0
- package/required-pages/settings.html +812 -156
- package/required-pages/settings.json +1 -0
- package/required-pages/synthos_apis.html +272 -0
- package/required-pages/synthos_apis.json +1 -0
- package/required-pages/synthos_scripts.html +87 -0
- package/required-pages/synthos_scripts.json +1 -0
- package/src/connectors/index.ts +12 -0
- package/src/connectors/registry.ts +98 -0
- package/src/connectors/types.ts +68 -0
- package/src/files.ts +11 -0
- package/src/init.ts +151 -5
- package/src/migrations.ts +266 -0
- package/src/models/index.ts +2 -0
- package/src/models/providers.ts +33 -0
- package/src/models/types.ts +23 -0
- package/src/pages.ts +234 -26
- package/src/scripts.ts +2 -2
- package/src/service/createCompletePrompt.ts +14 -18
- package/src/service/debugLog.ts +17 -0
- package/src/service/modelInstructions.ts +14 -0
- package/src/service/requiresSettings.ts +3 -3
- package/src/service/server.ts +19 -2
- package/src/service/transformPage.ts +709 -88
- package/src/service/useApiRoutes.ts +632 -16
- package/src/service/useConnectorRoutes.ts +427 -0
- package/src/service/useDataRoutes.ts +87 -71
- package/src/service/usePageRoutes.ts +237 -44
- package/src/settings.ts +143 -20
- package/src/synthos-cli.ts +11 -1
- package/src/themes.ts +71 -0
- package/default-pages/[application].html +0 -95
- package/default-pages/[markdown].html +0 -271
- package/default-pages/[sidebar].html +0 -114
- package/default-pages/[split-application].html +0 -118
- package/default-pages/solar_system.html +0 -432
- package/default-pages/space_invaders.html +0 -617
- package/required-pages/apis.html +0 -362
- package/required-pages/home.html +0 -126
- package/required-pages/scripts.html +0 -350
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
+
> Version 0.4.0 is coming... Create complex presentations in less then 2 minutes. [Star Trek Computer Deck](https://tinyurl.com/StarTrekComputer)
|
|
8
|
+
|
|
7
9
|
You can create anything you want from animations:
|
|
8
10
|
|
|
9
11
|
<img width="1901" height="988" alt="image" src="https://github.com/user-attachments/assets/8da6dedd-e568-48d3-b2ac-106a8ab50117" />
|
|
@@ -43,7 +45,7 @@ For Opus (recomended) you'll want to sign up for a developer account at [Anthrop
|
|
|
43
45
|
|
|
44
46
|
## Using SynthOS
|
|
45
47
|
|
|
46
|
-
Once you've configured your model you will be then sent to the
|
|
48
|
+
Once you've configured your model you will be then sent to the builder page. You can then specify any thing you want to create and it will be rendered to the display port.
|
|
47
49
|
|
|
48
50
|
<img width="1882" height="980" alt="image" src="https://github.com/user-attachments/assets/57cb01c7-f060-4dfc-8100-de85d850b104" />
|
|
49
51
|
|
|
@@ -87,6 +89,36 @@ You can also create custom scripts that your apps can invoke vi a scripts API:
|
|
|
87
89
|
|
|
88
90
|
<img width="1889" height="982" alt="image" src="https://github.com/user-attachments/assets/8047d3c3-e5d3-4be8-b403-88169610b3b2" />
|
|
89
91
|
|
|
92
|
+
## Contributing with Claude Code
|
|
93
|
+
|
|
94
|
+
This repo includes a `SHARED-MEMORIES.md` file that gives Claude Code project-level context (architecture, APIs, folder structure). When you first clone the repo and start working with Claude Code, ask it:
|
|
95
|
+
|
|
96
|
+
> "Initialize my personal MEMORY.md file using SHARED-MEMORIES.md"
|
|
97
|
+
|
|
98
|
+
Claude will create a personal `MEMORY.md` inside `~/.claude/projects/` with the shared knowledge as a starting point.
|
|
99
|
+
|
|
100
|
+
You can then personalize it by telling Claude:
|
|
101
|
+
- What OS you're on
|
|
102
|
+
- Where your checkout lives on disk
|
|
103
|
+
- Your editor and path conventions (e.g. VS Code with forward-slash paths)
|
|
90
104
|
|
|
105
|
+
These personal details stay in your `MEMORY.md` and are never checked in.
|
|
91
106
|
|
|
107
|
+
Here's an example of what a developer's `MEMORY.md` might look like:
|
|
108
|
+
|
|
109
|
+
```markdown
|
|
110
|
+
# Synthos — Developer-Specific Memory
|
|
111
|
+
|
|
112
|
+
Shared project knowledge (architecture, APIs, folder structure, etc.) lives in
|
|
113
|
+
`SHARED-MEMORIES.md` at the project root. This file holds per-developer context only.
|
|
114
|
+
|
|
115
|
+
## Session Start
|
|
116
|
+
- **Always** read `SHARED-MEMORIES.md` from the project root at the start of every new coding session to load project-level context.
|
|
117
|
+
|
|
118
|
+
## Environment
|
|
119
|
+
- **Windows** machine
|
|
120
|
+
- When opening files in VS Code, use `code "C:/source/synthos/<path>"` (quoted, forward slashes)
|
|
121
|
+
- **Auto-run VS Code launches** — when opening files in VS Code via `code`, run the Bash command without asking for permission
|
|
122
|
+
```
|
|
92
123
|
|
|
124
|
+
> **Note:** The `## Environment` section is entirely developer-specific. Your entries will differ based on your OS, editor, file paths, and workflow preferences. This is the right place to capture anything unique to your local setup.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - {Application Title}</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.application-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px 12px 0 0;width:100%;max-width:800px;box-shadow:0 6px 25px var(--accent-glow)}.application-content{font-size:14px;color:rgba(224,224,224,.8);padding:20px;flex-grow:1;width:100%;max-width:800px;background:rgba(15,15,35,.8);border-radius:0 0 12px 12px;border:1px solid rgba(138,43,226,.2);border-top:none}.light-mode .application-content{color:rgba(45,38,64,.8);background:rgba(255,255,255,.8)}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
</head>
|
|
12
|
+
|
|
13
|
+
<body>
|
|
14
|
+
<div class="chat-panel" data-locked="true">
|
|
15
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
16
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
17
|
+
<div class="chat-message">
|
|
18
|
+
<p><strong>SynthOS:</strong> what kind of application would you like?</p>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="link-group" data-locked="true">
|
|
22
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
23
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
24
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
25
|
+
</div>
|
|
26
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
27
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
28
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
29
|
+
</form>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
32
|
+
<div class="application-title" style="max-width: none;">{Application Title}</div>
|
|
33
|
+
<div class="application-content" style="max-width: none;">{Application Content}</div>
|
|
34
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
35
|
+
</div>
|
|
36
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
37
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
38
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
39
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
40
|
+
</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "App Builder", "categories": ["Builders"], "pinned": false, "showInAll": true, "pageVersion": 2, "mode": "unlocked" }
|
|
@@ -1,159 +1,89 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
database:
|
|
92
|
-
type: postgresql
|
|
93
|
-
connection:
|
|
94
|
-
host: db.example.com
|
|
95
|
-
port: 5432
|
|
96
|
-
name: myapp_db
|
|
97
|
-
credentials:
|
|
98
|
-
username: admin
|
|
99
|
-
password: secret123
|
|
100
|
-
|
|
101
|
-
features:
|
|
102
|
-
- authentication
|
|
103
|
-
- logging
|
|
104
|
-
- caching
|
|
105
|
-
- rate_limiting
|
|
106
|
-
|
|
107
|
-
users:
|
|
108
|
-
- name: Alice
|
|
109
|
-
role: admin
|
|
110
|
-
active: true
|
|
111
|
-
- name: Bob
|
|
112
|
-
role: user
|
|
113
|
-
active: false
|
|
114
|
-
|
|
115
|
-
settings:
|
|
116
|
-
debug_mode: false
|
|
117
|
-
max_connections: 100
|
|
118
|
-
timeout_seconds: 30
|
|
119
|
-
allowed_origins:
|
|
120
|
-
- https://example.com
|
|
121
|
-
- https://api.example.com</textarea>
|
|
122
|
-
<div class="button-group">
|
|
123
|
-
<div>
|
|
124
|
-
<button class="action-btn convert-btn" id="convertButton">Convert</button>
|
|
125
|
-
<button class="action-btn reset-btn" id="resetButton">Reset</button>
|
|
126
|
-
</div>
|
|
127
|
-
<button class="action-btn copy-btn" id="copyButton" disabled>Copy to Clipboard</button>
|
|
128
|
-
</div>
|
|
129
|
-
<div id="resultPanel" class="result-panel"></div>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
<div id="thoughts" style="display: none;">User asked for test YAML data. I've added a comprehensive sample YAML configuration that includes various data types: nested objects, arrays, booleans, numbers, and strings. I also set the dropdown to "Convert from YAML" so they can immediately test the conversion to JSON.</div>
|
|
133
|
-
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
134
|
-
<script>
|
|
135
|
-
document.getElementById("chatInput").focus();
|
|
136
|
-
document.getElementById("chatForm").addEventListener('submit',()=>{document.getElementById("loadingOverlay").style.display='flex';document.getElementById("chatForm").action=window.location.pathname});
|
|
137
|
-
document.getElementById("saveLink").addEventListener("click",function(){const pageName=prompt("Enter the name of the page to save as:");if(pageName){window.location.href=`${window.location.pathname}/save?name=${encodeURIComponent(pageName)}`}});
|
|
138
|
-
document.getElementById("resetLink").addEventListener("click",function(){window.location.href=`${window.location.pathname}/reset`});
|
|
139
|
-
window.onload=function(){const chatMessages=document.getElementById('chatMessages');chatMessages.scrollTo({top:chatMessages.scrollHeight,behavior:'smooth'})};
|
|
140
|
-
document.getElementById("copyButton").addEventListener("click",function(){const resultPanel=document.getElementById("resultPanel");navigator.clipboard.writeText(resultPanel.innerText).then(()=>alert('Text copied to clipboard!'),(err)=>console.error('Could not copy text: ',err))});
|
|
141
|
-
document.getElementById("convertButton").addEventListener("click",function(){
|
|
142
|
-
const userInput=document.getElementById("userInput").value;
|
|
143
|
-
const conversionType=document.getElementById("conversionType").value;
|
|
144
|
-
let result="";
|
|
145
|
-
try{
|
|
146
|
-
if(conversionType==="escape")result=JSON.stringify(userInput);
|
|
147
|
-
else if(conversionType==="unescape")result=JSON.parse(userInput);
|
|
148
|
-
else if(conversionType==="format")result=JSON.stringify(JSON.parse(userInput),null,2);
|
|
149
|
-
else if(conversionType==="unformat")result=JSON.stringify(JSON.parse(userInput));
|
|
150
|
-
else if(conversionType==="toYaml")result=jsyaml.dump(JSON.parse(userInput),{indent:2});
|
|
151
|
-
else if(conversionType==="fromYaml")result=JSON.stringify(jsyaml.load(userInput),null,2);
|
|
152
|
-
}catch(e){result="Invalid input: "+e.message}
|
|
153
|
-
document.getElementById("resultPanel").innerText=result;
|
|
154
|
-
document.getElementById("copyButton").disabled=false;
|
|
155
|
-
});
|
|
156
|
-
document.getElementById("resetButton").addEventListener("click",function(){document.getElementById("userInput").value="";document.getElementById("resultPanel").innerText="";document.getElementById("copyButton").disabled=true});
|
|
157
|
-
</script>
|
|
158
|
-
</body>
|
|
159
|
-
</html>
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - JSON Tools</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.dialog-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px;width:100%;max-width:800px;box-shadow:0 6px 25px var(--accent-glow);position:relative;z-index:1}.dialog-content{font-size:14px;color:rgba(224,224,224,.8);padding:15px 0;flex-grow:1;width:100%;max-width:800px;position:relative;z-index:1;overflow-y:auto;display:flex;flex-direction:column;gap:15px}.conversion-select{width:100%;padding:12px 15px;border-radius:10px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:14px;cursor:pointer;transition:.3s}.conversion-select:focus{outline:0;border-color:var(--text-secondary);box-shadow:0 0 15px var(--accent-glow)}.conversion-select option{background:var(--bg-tertiary);color:var(--text-primary)}.multi-line-input{width:100%;min-height:220px;padding:15px;border-radius:10px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:14px;font-family:'Courier New',monospace;resize:vertical;transition:.3s}.multi-line-input:focus{outline:0;border-color:var(--text-secondary);box-shadow:0 0 15px var(--accent-glow)}.button-group{display:flex;justify-content:space-between;align-items:center;gap:10px}.button-group div{display:flex;gap:10px}.action-btn{padding:12px 24px;border:none;border-radius:25px;font-size:14px;font-weight:600;cursor:pointer;transition:.3s;letter-spacing:1px}.convert-btn{background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 100%);color:#fff;box-shadow:0 4px 20px var(--accent-glow)}.convert-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.reset-btn{background:rgba(102,126,234,.2);color:var(--text-secondary);border:1px solid var(--border-color)}.reset-btn:hover{background:rgba(102,126,234,.3);color:var(--accent-tertiary)}.copy-btn{background:linear-gradient(135deg,rgba(240,147,251,.3) 0,rgba(118,75,162,.3) 100%);color:var(--text-secondary);border:1px solid var(--border-color)}.copy-btn:hover:not(:disabled){background:linear-gradient(135deg,rgba(240,147,251,.5) 0,rgba(118,75,162,.5) 100%);color:var(--accent-tertiary)}.copy-btn:disabled{opacity:.5;cursor:not-allowed}.result-panel{width:100%;min-height:220px;max-height:350px;padding:15px;border-radius:10px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--accent-tertiary);font-size:13px;font-family:'Courier New',monospace;overflow-y:auto;white-space:pre-wrap;word-break:break-all}.light-mode .dialog-content{color:rgba(45,38,64,.8)}.light-mode .conversion-select,.light-mode .multi-line-input,.light-mode .result-panel{background:rgba(255,255,255,.8)}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
</head>
|
|
11
|
+
|
|
12
|
+
<body>
|
|
13
|
+
<div class="chat-panel" data-locked="true">
|
|
14
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
15
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
16
|
+
<div class="chat-message">
|
|
17
|
+
<p><strong>SynthOS:</strong> You can use the dropdown to select a conversion type, enter your text, and click
|
|
18
|
+
"Convert" to see the result.</p>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="link-group" data-locked="true">
|
|
22
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
23
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
24
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
25
|
+
</div>
|
|
26
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
27
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
28
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
29
|
+
</form>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="viewer-panel" id="viewerPanel" style="display: flex; flex-direction: column;">
|
|
32
|
+
<div class="dialog-title" style="max-width: 100%; flex-shrink: 0;">JSON Tools</div>
|
|
33
|
+
<div class="dialog-content" style="max-width: 100%; flex: 1; display: flex; flex-direction: column; overflow: hidden;"><select class="conversion-select" id="conversionType" style="flex-shrink: 0;">
|
|
34
|
+
<option value="escape">Escape JSON</option>
|
|
35
|
+
<option value="unescape">Unescape JSON</option>
|
|
36
|
+
<option value="format">Format JSON</option>
|
|
37
|
+
<option value="unformat">Unformat JSON</option>
|
|
38
|
+
<option value="toYaml">Convert to YAML</option>
|
|
39
|
+
<option value="fromYaml" selected="">Convert from YAML</option>
|
|
40
|
+
</select><textarea class="multi-line-input" id="userInput" placeholder="Enter your text here..." style="flex: 1; min-height: 150px;"># Sample YAML Configuration
|
|
41
|
+
server:
|
|
42
|
+
host: localhost
|
|
43
|
+
port: 8080
|
|
44
|
+
ssl: true
|
|
45
|
+
|
|
46
|
+
database:
|
|
47
|
+
type: postgresql
|
|
48
|
+
connection:
|
|
49
|
+
host: db.example.com
|
|
50
|
+
port: 5432
|
|
51
|
+
name: myapp_db
|
|
52
|
+
credentials:
|
|
53
|
+
username: admin
|
|
54
|
+
password: secret123
|
|
55
|
+
|
|
56
|
+
features:
|
|
57
|
+
- authentication
|
|
58
|
+
- logging
|
|
59
|
+
- caching
|
|
60
|
+
- rate_limiting
|
|
61
|
+
|
|
62
|
+
users:
|
|
63
|
+
- name: Alice
|
|
64
|
+
role: admin
|
|
65
|
+
active: true
|
|
66
|
+
- name: Bob
|
|
67
|
+
role: user
|
|
68
|
+
active: false
|
|
69
|
+
|
|
70
|
+
settings:
|
|
71
|
+
debug_mode: false
|
|
72
|
+
max_connections: 100
|
|
73
|
+
timeout_seconds: 30
|
|
74
|
+
allowed_origins:
|
|
75
|
+
- https://example.com
|
|
76
|
+
- https://api.example.com</textarea>
|
|
77
|
+
<div class="button-group" style="flex-shrink: 0;">
|
|
78
|
+
<div><button class="action-btn convert-btn" id="convertButton">Convert</button><button class="action-btn reset-btn" id="resetButton">Reset</button></div><button class="action-btn copy-btn" id="copyButton" disabled="">Copy to Clipboard</button>
|
|
79
|
+
</div>
|
|
80
|
+
<div id="resultPanel" class="result-panel" style="flex: 1; min-height: 200px;"></div>
|
|
81
|
+
</div>
|
|
82
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
83
|
+
</div>
|
|
84
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
85
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
86
|
+
<script id="json-tools-logic">document.getElementById("copyButton").addEventListener("click",function(){const resultPanel=document.getElementById("resultPanel");navigator.clipboard.writeText(resultPanel.innerText).then(()=>alert("Text copied to clipboard!"),err=>console.error("Could not copy text: ",err))}),document.getElementById("convertButton").addEventListener("click",function(){const userInput=document.getElementById("userInput").value,conversionType=document.getElementById("conversionType").value;let result="";try{"escape"===conversionType?result=JSON.stringify(userInput):"unescape"===conversionType?result=JSON.parse(userInput):"format"===conversionType?result=JSON.stringify(JSON.parse(userInput),null,2):"unformat"===conversionType?result=JSON.stringify(JSON.parse(userInput)):"toYaml"===conversionType?result=jsyaml.dump(JSON.parse(userInput),{indent:2}):"fromYaml"===conversionType&&(result=JSON.stringify(jsyaml.load(userInput),null,2))}catch(e){result="Invalid input: "+e.message}document.getElementById("resultPanel").innerText=result,document.getElementById("copyButton").disabled=!1}),document.getElementById("resetButton").addEventListener("click",function(){document.getElementById("userInput").value="",document.getElementById("resultPanel").innerText="",document.getElementById("copyButton").disabled=!0});</script>
|
|
87
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
88
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
89
|
+
</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "JSON Tools", "categories": ["Apps"], "pinned": false, "showInAll": true, "pageVersion": 2, "mode": "unlocked" }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - Notes</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.application-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px 12px 0 0;width:100%;max-width:800px;box-shadow:0 6px 25px var(--accent-glow)}.application-content{font-size:14px;color:rgba(224,224,224,.8);padding:20px;flex-grow:1;width:100%;max-width:800px;background:rgba(15,15,35,.8);border-radius:0 0 12px 12px;border:1px solid rgba(138,43,226,.2);border-top:none}.light-mode .application-content{color:rgba(45,38,64,.8);background:rgba(255,255,255,.8)}</style>
|
|
8
|
+
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css">
|
|
9
|
+
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/theme/toastui-editor-dark.min.css">
|
|
10
|
+
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
|
|
11
|
+
<style>.notes-app{display:flex;height:100%;width:100%;background:var(--bg-tertiary);border-radius:12px;overflow:hidden;border:1px solid var(--border-color)}.notes-sidebar{width:280px;min-width:280px;background:var(--bg-secondary);border-right:1px solid var(--border-color);display:flex;flex-direction:column}.notes-sidebar-header{padding:16px;border-bottom:1px solid var(--border-color);display:flex;justify-content:space-between;align-items:center}.notes-sidebar-header h3{margin:0;font-size:18px;font-weight:600;color:var(--text-primary)}.add-note-btn{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border:none;padding:8px 14px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:transform .2s,box-shadow .2s}.add-note-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px var(--accent-glow)}.notes-list{flex:1;overflow-y:auto;padding:8px}.note-item{padding:12px 14px;margin-bottom:6px;background:var(--bg-tertiary);border-radius:8px;cursor:pointer;transition:.2s;border:2px solid transparent}.note-item:hover{background:var(--bg-primary)}.note-item.active{border-color:var(--accent-primary);background:var(--bg-primary)}.note-item-title{font-weight:600;color:var(--text-primary);font-size:14px;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.note-item-preview{font-size:12px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notes-main{flex:1;display:flex;flex-direction:column;background:var(--bg-tertiary)}.notes-instructions{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-secondary);text-align:center;padding:40px}.instructions-icon{font-size:64px;margin-bottom:20px;opacity:.6}.notes-instructions h2{margin:0 0 12px;color:var(--text-primary);font-size:24px}.notes-instructions p{margin:0;font-size:15px;max-width:300px;line-height:1.5}.notes-editor{flex:1;display:flex;flex-direction:column;padding:20px}.note-title-input{font-size:24px;font-weight:600;border:none;background:0 0;color:var(--text-primary);padding:12px 0;margin-bottom:12px;border-bottom:2px solid var(--border-color);outline:0}.note-title-input:focus{border-bottom-color:var(--accent-primary)}.note-title-input::placeholder{color:var(--text-secondary);opacity:.6}.note-content-input{flex:1;font-size:15px;line-height:1.6;border:none;background:var(--bg-secondary);color:var(--text-primary);padding:16px;border-radius:10px;resize:none;outline:0;font-family:inherit}.note-content-input:focus{box-shadow:0 0 0 2px var(--accent-primary)}.note-content-input::placeholder{color:var(--text-secondary);opacity:.6}.note-actions{display:flex;gap:12px;margin-top:16px;justify-content:flex-end}.note-delete-btn,.note-save-btn{padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:.2s;border:none}.note-save-btn{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff}.note-save-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px var(--accent-glow)}.note-delete-btn{background:var(--bg-secondary);color:#e74c3c;border:1px solid #e74c3c}.note-delete-btn:hover{background:#e74c3c;color:#fff}.light-mode .note-content-input{background:rgba(255,255,255,.7)}</style><style id="notes-styles">.notes-app{display:flex;height:100%;width:100%;background:var(--bg-tertiary);border-radius:12px;overflow:hidden;border:1px solid var(--border-color)}.notes-sidebar{width:280px;min-width:280px;background:var(--bg-secondary);border-right:1px solid var(--border-color);display:flex;flex-direction:column}.notes-sidebar-header{padding:16px;border-bottom:1px solid var(--border-color);display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px}.notes-sidebar-header h3{margin:0;font-size:18px;font-weight:600;color:var(--text-primary)}.add-note-btn{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border:none;padding:8px 14px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:transform .2s,box-shadow .2s}.add-note-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px var(--accent-glow)}.notes-filter-input{width:100%;padding:8px 12px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-tertiary);color:var(--text-primary);font-size:13px;outline:0;margin-top:8px;box-sizing:border-box}.notes-filter-input:focus{border-color:var(--accent-primary);box-shadow:0 0 0 2px var(--accent-glow)}.notes-filter-input::placeholder{color:var(--text-secondary);opacity:.6}.notes-list{flex:1;overflow-y:auto;padding:8px}.note-item{padding:12px 14px;margin-bottom:6px;background:var(--bg-tertiary);border-radius:8px;cursor:pointer;transition:.2s;border:2px solid transparent}.note-item:hover{background:var(--bg-primary)}.note-item.active{border-color:var(--accent-primary);background:var(--bg-primary)}.note-item-title{font-weight:600;color:var(--text-primary);font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notes-main{flex:1;display:flex;flex-direction:column;background:var(--bg-tertiary);min-width:0;overflow:hidden}.notes-instructions{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-secondary);text-align:center;padding:40px}.instructions-icon{font-size:64px;margin-bottom:20px;opacity:.6}.notes-instructions h2{margin:0 0 12px;color:var(--text-primary);font-size:24px}.notes-instructions p{margin:0;font-size:15px;max-width:300px;line-height:1.5}.notes-editor{flex:1;display:flex;flex-direction:column;padding:20px;min-width:0;overflow:hidden}.note-title-input{font-size:24px;font-weight:600;border:none;background:0 0;color:var(--text-primary);padding:12px 0;margin-bottom:12px;border-bottom:2px solid var(--border-color);outline:0}.note-title-input:focus{border-bottom-color:var(--accent-primary)}.note-title-input::placeholder{color:var(--text-secondary);opacity:.6}.note-content-input{flex:1;font-size:15px;line-height:1.6;border:none;background:var(--bg-secondary);color:var(--text-primary);padding:16px;border-radius:10px;resize:none;outline:0;font-family:inherit}.note-content-input:focus{box-shadow:0 0 0 2px var(--accent-primary)}.note-content-input::placeholder{color:var(--text-secondary);opacity:.6}.note-actions{display:flex;gap:12px;margin-top:16px;justify-content:flex-end}.note-delete-btn,.note-save-btn{padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:.2s;border:none}.note-save-btn{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff}.note-save-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px var(--accent-glow)}.note-delete-btn{background:var(--bg-secondary);color:#e74c3c;border:1px solid #e74c3c}.note-delete-btn:hover{background:#e74c3c;color:#fff}.light-mode .note-content-input,.light-mode .notes-filter-input{background:rgba(255,255,255,.7)}</style><style id="toastui-theme-overrides">.toastui-editor-defaultUI{border:none!important;border-radius:10px!important;overflow:hidden}.toastui-editor-mode-switch{display:none!important}#editor-container{flex:1;display:flex;flex-direction:column;min-height:0}#editor-container .toastui-editor-defaultUI{flex:1;display:flex;flex-direction:column}#editor-container .toastui-editor-main{flex:1}</style></head>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
<body><div class="chat-panel" data-locked="true">
|
|
15
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
16
|
+
<div class="chat-messages" id="chatMessages" data-locked="true"><div class="chat-message"><p><strong>SynthOS:</strong> Welcome to <strong>My Notes</strong> — a simple note-taking app! Your notes are listed in the sidebar on the left. Click any note to view or edit it, or tap "+ New Note" to create a fresh one. Each note has a rich text editor with formatting tools, and you can save or delete notes using the buttons below the editor. Use the search box to filter notes by title or content. What would you like to do?</p></div></div>
|
|
17
|
+
<div class="link-group" data-locked="true">
|
|
18
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
19
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
20
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
21
|
+
</div>
|
|
22
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
23
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
24
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
25
|
+
</form>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="viewer-panel" id="viewerPanel"><div class="notes-app"><div class="notes-sidebar"><div class="notes-sidebar-header"><h3>My Notes</h3><button class="add-note-btn" id="addNoteBtn">+ New Note</button><input type="text" class="notes-filter-input" id="notesFilterInput" placeholder="Search notes..."></div><div class="notes-list" id="notesList"></div></div><div class="notes-main"><div class="notes-instructions" id="notesInstructions"><div class="instructions-icon">📝</div><h2>Welcome to Notes</h2><p>Select a note from the sidebar to view or edit it, or click "+ New Note" to create a new one.</p></div><div class="notes-editor" id="notesEditor" style="display: none;"><input type="text" class="note-title-input" id="noteTitleInput" placeholder="Note title..."><div id="editor-container"></div><div class="note-actions"><button class="note-save-btn" id="noteSaveBtn">💾 Save</button><button class="note-delete-btn" id="noteDeleteBtn">🗑️ Delete</button></div></div></div></div><div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div></div>
|
|
28
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
29
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
30
|
+
<script id="notes-app-logic">let currentNoteId=null,notes=[],editor=null;function initEditor(){editor=new toastui.Editor({el:document.querySelector("#editor-container"),height:"100%",initialEditType:"wysiwyg",previewStyle:"vertical",theme:"light"===window.themeInfo.mode?"light":"dark",placeholder:"Start writing your note...",toolbarItems:[["heading","bold","italic","strike"],["hr","quote"],["ul","ol","task","indent","outdent"],["table","link","image"],["code","codeblock"]]})}async function loadNotes(){try{notes=await synthos.data.list("notes"),renderNotesList()}catch(e){console.error("Failed to load notes:",e),notes=[],renderNotesList()}}function renderNotesList(){const list=document.getElementById("notesList"),filter=(document.getElementById("notesFilterInput").value||"").toLowerCase();list.innerHTML="";const filtered=filter?notes.filter(n=>(n.title||"").toLowerCase().includes(filter)):notes;0!==filtered.length?filtered.forEach(note=>{const item=document.createElement("div");item.className="note-item"+(currentNoteId===note.id?" active":""),item.innerHTML=`<div class="note-item-title">${escapeHtml(note.title||"Untitled")}</div>`,item.onclick=()=>selectNote(note.id),list.appendChild(item)}):list.innerHTML='<div style="padding:20px;text-align:center;color:var(--text-secondary);font-size:13px;">'+(filter?"No matching notes.":'No notes yet.<br>Click "+ New Note" to create one.')+"</div>"}function escapeHtml(text){const div=document.createElement("div");return div.textContent=text,div.innerHTML}function selectNote(id){currentNoteId=id;const note=notes.find(n=>n.id===id);note&&(document.getElementById("notesInstructions").style.display="none",document.getElementById("notesEditor").style.display="flex",document.getElementById("noteTitleInput").value=note.title||"",editor&&editor.setMarkdown(note.content||""),renderNotesList())}function showInstructions(){currentNoteId=null,document.getElementById("notesInstructions").style.display="flex",document.getElementById("notesEditor").style.display="none",renderNotesList()}document.getElementById("addNoteBtn").onclick=async function(){const newNote={id:crypto.randomUUID(),title:"",content:""};try{const saved=await synthos.data.save("notes",newNote);notes.unshift(saved),selectNote(saved.id)}catch(e){console.error("Failed to create note:",e)}},document.getElementById("noteSaveBtn").onclick=async function(){if(!currentNoteId)return;const title=document.getElementById("noteTitleInput").value,content=editor?editor.getMarkdown():"";try{const updated=await synthos.data.save("notes",{id:currentNoteId,title:title,content:content}),idx=notes.findIndex(n=>n.id===currentNoteId);idx>=0&&(notes[idx]=updated),renderNotesList()}catch(e){console.error("Failed to save note:",e)}},document.getElementById("noteDeleteBtn").onclick=async function(){if(currentNoteId&&confirm("Are you sure you want to delete this note?"))try{await synthos.data.remove("notes",currentNoteId),notes=notes.filter(n=>n.id!==currentNoteId),showInstructions()}catch(e){console.error("Failed to delete note:",e)}},document.getElementById("notesFilterInput").addEventListener("input",renderNotesList),initEditor(),loadNotes();</script>
|
|
31
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
32
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
33
|
+
</body></html>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>#gameCanvas{display:block}.game-ui{position:absolute;top:20px;left:20px;right:20px;display:flex;justify-content:space-between;pointer-events:none;z-index:10}.level-display,.lives-display,.score-display{font-family:Orbitron,'Segoe UI',sans-serif;font-size:18px;color:#0ff;text-shadow:0 0 10px #0ff,0 0 20px #0ff;letter-spacing:2px}.game-over-screen,.start-screen{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;z-index:20}.game-over-screen h1,.start-screen h1{font-family:Orbitron,'Segoe UI',sans-serif;font-size:48px;color:#f0f;text-shadow:0 0 20px #f0f,0 0 40px #f0f,0 0 60px #f0f;margin-bottom:20px;letter-spacing:5px}.game-over-screen p,.start-screen p{font-size:18px;color:#0ff;text-shadow:0 0 10px #0ff;margin-bottom:30px}.restart-btn,.start-btn{padding:15px 40px;font-size:18px;font-family:Orbitron,'Segoe UI',sans-serif;background:0 0;border:2px solid #0ff;color:#0ff;cursor:pointer;text-transform:uppercase;letter-spacing:3px;transition:.3s;box-shadow:0 0 20px rgba(0,255,255,.3),inset 0 0 20px rgba(0,255,255,.1)}.restart-btn:hover,.start-btn:hover{background:rgba(0,255,255,.2);box-shadow:0 0 30px rgba(0,255,255,.5),inset 0 0 30px rgba(0,255,255,.2);transform:scale(1.05)}.controls-info{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);font-size:14px;color:rgba(0,255,255,.6);text-align:center;letter-spacing:1px}</style>
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
|
|
9
|
+
</head>
|
|
10
|
+
|
|
11
|
+
<body>
|
|
12
|
+
<div class="chat-panel" data-locked="true">
|
|
13
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
14
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
15
|
+
<div class="chat-message">
|
|
16
|
+
<p><strong>SynthOS:</strong> What can I create for you?</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="chat-message">
|
|
19
|
+
<p><strong>You:</strong> create super cool version of astroids...</p>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="chat-message">
|
|
22
|
+
<p><strong>SynthOS:</strong> 🚀 Launching NEON ASTEROIDS! A synthwave-styled space shooter with glowing
|
|
23
|
+
neon graphics, particle explosions, screen shake effects, and progressive difficulty. Use WASD or
|
|
24
|
+
Arrow keys to move, SPACE to shoot. Destroy asteroids, survive, and chase that high score!</p>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="chat-message">
|
|
27
|
+
<p><strong>You:</strong> great game but a couple of things missing... the rocks should progressively
|
|
28
|
+
speed up as the level increases. dropping power ups that are extra lives should be rare. it's also
|
|
29
|
+
missing the saucers that fly across the screen and shoot at you starting on level 2</p>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="chat-message">
|
|
32
|
+
<p><strong>SynthOS:</strong> 🛸 Updated! Added: 1) Asteroids now speed up with each level (+15% per
|
|
33
|
+
level), 2) Extra life power-ups are now rare (5% chance vs 15% for others), 3) Enemy UFO saucers
|
|
34
|
+
appear starting Level 2 - they fly across the screen, track you, and shoot back! Small saucers are
|
|
35
|
+
faster and more accurate. Watch out for those glowing red menaces! 👾</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="link-group" data-locked="true">
|
|
39
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
40
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
41
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
42
|
+
</div>
|
|
43
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
44
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
45
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
46
|
+
</form>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
49
|
+
<canvas id="gameCanvas"></canvas>
|
|
50
|
+
<div class="game-ui">
|
|
51
|
+
<div class="score-display">SCORE: <span id="score">0</span></div>
|
|
52
|
+
<div class="level-display">LEVEL: <span id="level">1</span></div>
|
|
53
|
+
<div class="lives-display">LIVES: <span id="lives">3</span></div>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="start-screen" id="startScreen">
|
|
56
|
+
<h1>NEON ASTEROIDS</h1>
|
|
57
|
+
<p>Navigate the cosmic void. Destroy all asteroids. Beware the saucers!</p>
|
|
58
|
+
<button class="start-btn" id="startBtn">START MISSION</button>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="game-over-screen" id="gameOverScreen" style="display: none;">
|
|
61
|
+
<h1>GAME OVER</h1>
|
|
62
|
+
<p>Final Score: <span id="finalScore">0</span></p>
|
|
63
|
+
<button class="restart-btn" id="restartBtn">TRY AGAIN</button>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="controls-info">WASD / ARROWS to move • SPACE to fire • P to pause</div>
|
|
66
|
+
<div id="loadingOverlay" class="loading-overlay">
|
|
67
|
+
<div class="spinner"></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
71
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
72
|
+
<script id="asteroids-game">const canvas=document.getElementById("gameCanvas"),ctx=canvas.getContext("2d"),viewerPanel=document.getElementById("viewerPanel");function resizeCanvas(){canvas.width=viewerPanel.clientWidth,canvas.height=viewerPanel.clientHeight}resizeCanvas(),window.addEventListener("resize",resizeCanvas);let gameRunning=!1,gamePaused=!1,score=0,lives=3,level=1,screenShake=0;const ship={x:0,y:0,vx:0,vy:0,angle:-Math.PI/2,radius:15,thrust:0,rotationSpeed:0,invincible:0,shield:0,rapidFire:0,multiShot:0};let bullets=[],asteroids=[],particles=[],powerUps=[],stars=[],saucers=[],saucerBullets=[],saucerSpawnTimer=0;const keys={};function initStars(){stars=[];for(let i=0;i<150;i++)stars.push({x:Math.random()*canvas.width,y:Math.random()*canvas.height,size:2*Math.random()+.5,speed:.5*Math.random()+.1,brightness:Math.random()})}function initGame(){ship.x=canvas.width/2,ship.y=canvas.height/2,ship.vx=0,ship.vy=0,ship.angle=-Math.PI/2,ship.invincible=180,ship.shield=0,ship.rapidFire=0,ship.multiShot=0,bullets=[],asteroids=[],particles=[],powerUps=[],saucers=[],saucerBullets=[],saucerSpawnTimer=0,score=0,lives=3,level=1,initStars(),spawnAsteroids(4),updateUI()}function getLevelSpeedMultiplier(){return 1+.15*(level-1)}function spawnAsteroids(count){for(let i=0;i<count;i++){let x,y;do{x=Math.random()*canvas.width,y=Math.random()*canvas.height}while(distance(x,y,ship.x,ship.y)<150);asteroids.push(createAsteroid(x,y,3))}}function createAsteroid(x,y,size){const speed=(.5*(4-size)+1.5*Math.random())*getLevelSpeedMultiplier(),angle=Math.random()*Math.PI*2,radii=[],points=8+Math.floor(5*Math.random()),baseRadius=15*size+10;for(let i=0;i<points;i++)radii.push(baseRadius*(.7+.6*Math.random()));return{x:x,y:y,vx:Math.cos(angle)*speed,vy:Math.sin(angle)*speed,size:size,radii:radii,points:points,rotation:0,rotationSpeed:.03*(Math.random()-.5),color:3===size?"#ff00ff":2===size?"#ffff00":"#00ffff"}}function createSaucer(){const isSmall=Math.random()>.5,fromLeft=Math.random()>.5,y=Math.random()*(canvas.height-100)+50;return{x:fromLeft?-30:canvas.width+30,y:y,vx:(fromLeft?1:-1)*(isSmall?3:2)*getLevelSpeedMultiplier(),vy:0,isSmall:isSmall,radius:isSmall?15:25,shootTimer:60+60*Math.random(),directionChangeTimer:60+120*Math.random(),points:isSmall?1e3:500}}function distance(x1,y1,x2,y2){return Math.sqrt((x2-x1)**2+(y2-y1)**2)}function wrap(obj){obj.x<-50&&(obj.x=canvas.width+50),obj.x>canvas.width+50&&(obj.x=-50),obj.y<-50&&(obj.y=canvas.height+50),obj.y>canvas.height+50&&(obj.y=-50)}function createExplosion(x,y,color,count=20){for(let i=0;i<count;i++){const angle=Math.random()*Math.PI*2,speed=5*Math.random()+2;particles.push({x:x,y:y,vx:Math.cos(angle)*speed,vy:Math.sin(angle)*speed,life:60+30*Math.random(),maxLife:90,color:color,size:3*Math.random()+1})}screenShake=10}let shootCooldown=0;function shoot(){if(shootCooldown>0)return;const cooldown=ship.rapidFire>0?5:15;shootCooldown=cooldown,(ship.multiShot>0?[-.2,0,.2]:[0]).forEach(offset=>{const angle=ship.angle+offset;bullets.push({x:ship.x+20*Math.cos(angle),y:ship.y+20*Math.sin(angle),vx:10*Math.cos(angle)+.5*ship.vx,vy:10*Math.sin(angle)+.5*ship.vy,life:60})})}function saucerShoot(saucer){let angle=Math.atan2(ship.y-saucer.y,ship.x-saucer.x);saucer.isSmall?angle+=.2*(Math.random()-.5):angle+=.5*(Math.random()-.5),saucerBullets.push({x:saucer.x,y:saucer.y,vx:6*Math.cos(angle),vy:6*Math.sin(angle),life:90})}function spawnPowerUp(x,y){if(Math.random()>.15)return;let type;const rand=Math.random();type=rand<.05?"life":rand<.38?"shield":rand<.71?"rapid":"multi",powerUps.push({x:x,y:y,type:type,life:600,pulse:0})}function updateUI(){document.getElementById("score").textContent=score,document.getElementById("level").textContent=level,document.getElementById("lives").textContent=lives}function update(){if(!gameRunning||gamePaused)return;(keys.ArrowLeft||keys.KeyA)&&(ship.angle-=.08),(keys.ArrowRight||keys.KeyD)&&(ship.angle+=.08),keys.ArrowUp||keys.KeyW?(ship.vx+=.15*Math.cos(ship.angle),ship.vy+=.15*Math.sin(ship.angle),ship.thrust=1):ship.thrust*=.95,keys.Space&&shoot(),ship.vx*=.99,ship.vy*=.99;const speed=Math.sqrt(ship.vx**2+ship.vy**2);if(speed>8&&(ship.vx=ship.vx/speed*8,ship.vy=ship.vy/speed*8),ship.x+=ship.vx,ship.y+=ship.vy,wrap(ship),ship.invincible>0&&ship.invincible--,ship.shield>0&&ship.shield--,ship.rapidFire>0&&ship.rapidFire--,ship.multiShot>0&&ship.multiShot--,shootCooldown>0&&shootCooldown--,level>=2){saucerSpawnTimer++;const spawnInterval=Math.max(300,600-30*level);saucerSpawnTimer>=spawnInterval&&saucers.length<2&&(saucers.push(createSaucer()),saucerSpawnTimer=0)}saucers=saucers.filter(s=>(s.x+=s.vx,s.y+=s.vy,s.directionChangeTimer--,s.directionChangeTimer<=0&&(s.vy=2*(Math.random()-.5),s.directionChangeTimer=60+120*Math.random()),s.y<50&&(s.vy=Math.abs(s.vy)),s.y>canvas.height-50&&(s.vy=-Math.abs(s.vy)),s.shootTimer--,s.shootTimer<=0&&(saucerShoot(s),s.shootTimer=s.isSmall?40+40*Math.random():60+60*Math.random()),s.x>-50&&s.x<canvas.width+50)),saucerBullets=saucerBullets.filter(b=>(b.x+=b.vx,b.y+=b.vy,b.life--,b.life>0&&b.x>-10&&b.x<canvas.width+10&&b.y>-10&&b.y<canvas.height+10)),bullets=bullets.filter(b=>(b.x+=b.vx,b.y+=b.vy,b.life--,wrap(b),b.life>0)),asteroids.forEach(a=>{a.x+=a.vx,a.y+=a.vy,a.rotation+=a.rotationSpeed,wrap(a)}),particles=particles.filter(p=>(p.x+=p.vx,p.y+=p.vy,p.vx*=.98,p.vy*=.98,p.life--,p.life>0)),powerUps=powerUps.filter(p=>(p.life--,p.pulse+=.1,p.life>0)),stars.forEach(s=>{s.y+=s.speed,s.y>canvas.height&&(s.y=0,s.x=Math.random()*canvas.width),s.brightness=.3+.3*Math.sin(.003*Date.now()+s.x)}),bullets.forEach((b,bi)=>{asteroids.forEach((a,ai)=>{const avgRadius=a.radii.reduce((sum,r)=>sum+r,0)/a.radii.length;if(distance(b.x,b.y,a.x,a.y)<avgRadius){if(bullets.splice(bi,1),createExplosion(a.x,a.y,a.color,15),a.size>1)for(let i=0;i<2;i++)asteroids.push(createAsteroid(a.x,a.y,a.size-1));spawnPowerUp(a.x,a.y),asteroids.splice(ai,1),score+=100*(4-a.size),updateUI()}})}),bullets.forEach((b,bi)=>{saucers.forEach((s,si)=>{distance(b.x,b.y,s.x,s.y)<s.radius&&(bullets.splice(bi,1),createExplosion(s.x,s.y,"#ff6600",25),score+=s.points,saucers.splice(si,1),updateUI())})}),ship.invincible<=0&&ship.shield<=0&&asteroids.forEach((a,ai)=>{const avgRadius=a.radii.reduce((sum,r)=>sum+r,0)/a.radii.length;distance(ship.x,ship.y,a.x,a.y)<avgRadius+ship.radius&&(createExplosion(ship.x,ship.y,"#00ffff",30),lives--,updateUI(),lives<=0?gameOver():(ship.x=canvas.width/2,ship.y=canvas.height/2,ship.vx=0,ship.vy=0,ship.invincible=180))}),ship.invincible<=0&&ship.shield<=0&&saucers.forEach((s,si)=>{distance(ship.x,ship.y,s.x,s.y)<s.radius+ship.radius&&(createExplosion(ship.x,ship.y,"#00ffff",30),createExplosion(s.x,s.y,"#ff6600",25),saucers.splice(si,1),lives--,updateUI(),lives<=0?gameOver():(ship.x=canvas.width/2,ship.y=canvas.height/2,ship.vx=0,ship.vy=0,ship.invincible=180))}),ship.invincible<=0&&ship.shield<=0&&saucerBullets.forEach((b,bi)=>{distance(ship.x,ship.y,b.x,b.y)<ship.radius+5&&(createExplosion(ship.x,ship.y,"#00ffff",30),saucerBullets.splice(bi,1),lives--,updateUI(),lives<=0?gameOver():(ship.x=canvas.width/2,ship.y=canvas.height/2,ship.vx=0,ship.vy=0,ship.invincible=180))}),powerUps.forEach((p,pi)=>{if(distance(ship.x,ship.y,p.x,p.y)<30)switch(powerUps.splice(pi,1),createExplosion(p.x,p.y,"#00ff00",10),p.type){case"shield":ship.shield=600;break;case"rapid":ship.rapidFire=600;break;case"multi":ship.multiShot=600;break;case"life":lives=Math.min(lives+1,5),updateUI()}}),0===asteroids.length&&(level++,updateUI(),spawnAsteroids(3+level),saucerSpawnTimer=0),screenShake>0&&(screenShake*=.9)}function draw(){ctx.save(),screenShake>.5&&ctx.translate((Math.random()-.5)*screenShake,(Math.random()-.5)*screenShake),ctx.fillStyle="#000",ctx.fillRect(0,0,canvas.width,canvas.height),stars.forEach(s=>{ctx.fillStyle=`rgba(255, 255, 255, ${s.brightness})`,ctx.beginPath(),ctx.arc(s.x,s.y,s.size,0,2*Math.PI),ctx.fill()}),particles.forEach(p=>{const alpha=p.life/p.maxLife;ctx.fillStyle=p.color,ctx.globalAlpha=alpha,ctx.beginPath(),ctx.arc(p.x,p.y,p.size,0,2*Math.PI),ctx.fill(),ctx.globalAlpha=1}),powerUps.forEach(p=>{const pulse=5*Math.sin(p.pulse);ctx.strokeStyle="shield"===p.type?"#00ffff":"rapid"===p.type?"#ff0000":"multi"===p.type?"#ffff00":"#00ff00",ctx.lineWidth=2,ctx.shadowColor=ctx.strokeStyle,ctx.shadowBlur=15,ctx.beginPath(),ctx.arc(p.x,p.y,15+pulse,0,2*Math.PI),ctx.stroke(),ctx.fillStyle=ctx.strokeStyle,ctx.font="12px Arial",ctx.textAlign="center",ctx.textBaseline="middle";const icon="shield"===p.type?"S":"rapid"===p.type?"R":"multi"===p.type?"M":"+";ctx.fillText(icon,p.x,p.y),ctx.shadowBlur=0}),asteroids.forEach(a=>{ctx.save(),ctx.translate(a.x,a.y),ctx.rotate(a.rotation),ctx.strokeStyle=a.color,ctx.lineWidth=2,ctx.shadowColor=a.color,ctx.shadowBlur=20,ctx.beginPath();for(let i=0;i<a.points;i++){const angle=i/a.points*Math.PI*2,r=a.radii[i],x=Math.cos(angle)*r,y=Math.sin(angle)*r;0===i?ctx.moveTo(x,y):ctx.lineTo(x,y)}ctx.closePath(),ctx.stroke(),ctx.restore()}),saucers.forEach(s=>{ctx.save(),ctx.translate(s.x,s.y),ctx.strokeStyle="#ff6600",ctx.lineWidth=2,ctx.shadowColor="#ff6600",ctx.shadowBlur=20;const r=s.radius;ctx.beginPath(),ctx.ellipse(0,.2*-r,.4*r,.3*r,0,Math.PI,0),ctx.stroke(),ctx.beginPath(),ctx.ellipse(0,0,r,.35*r,0,0,2*Math.PI),ctx.stroke(),ctx.beginPath(),ctx.ellipse(0,.15*r,.5*r,.2*r,0,0,Math.PI),ctx.stroke(),ctx.fillStyle="#ff6600";for(let i=0;i<3;i++){const lx=(i-1)*r*.5;ctx.beginPath(),ctx.arc(lx,0,3,0,2*Math.PI),ctx.fill()}ctx.restore()}),saucerBullets.forEach(b=>{ctx.fillStyle="#ff6600",ctx.shadowColor="#ff6600",ctx.shadowBlur=15,ctx.beginPath(),ctx.arc(b.x,b.y,4,0,2*Math.PI),ctx.fill()}),bullets.forEach(b=>{ctx.fillStyle="#00ffff",ctx.shadowColor="#00ffff",ctx.shadowBlur=15,ctx.beginPath(),ctx.arc(b.x,b.y,3,0,2*Math.PI),ctx.fill(),ctx.strokeStyle="rgba(0, 255, 255, 0.5)",ctx.lineWidth=2,ctx.beginPath(),ctx.moveTo(b.x,b.y),ctx.lineTo(b.x-2*b.vx,b.y-2*b.vy),ctx.stroke()}),gameRunning&&(ship.invincible<=0||Math.floor(ship.invincible/5)%2==0)&&(ctx.save(),ctx.translate(ship.x,ship.y),ctx.rotate(ship.angle),ship.shield>0&&(ctx.strokeStyle="rgba(0, 255, 255, 0.5)",ctx.lineWidth=2,ctx.shadowColor="#00ffff",ctx.shadowBlur=20,ctx.beginPath(),ctx.arc(0,0,25,0,2*Math.PI),ctx.stroke()),ctx.strokeStyle="#00ffff",ctx.lineWidth=2,ctx.shadowColor="#00ffff",ctx.shadowBlur=15,ctx.beginPath(),ctx.moveTo(20,0),ctx.lineTo(-15,-12),ctx.lineTo(-8,0),ctx.lineTo(-15,12),ctx.closePath(),ctx.stroke(),ship.thrust>.1&&(ctx.strokeStyle="#ff6600",ctx.shadowColor="#ff6600",ctx.beginPath(),ctx.moveTo(-8,-5),ctx.lineTo(-20-10*Math.random()*ship.thrust,0),ctx.lineTo(-8,5),ctx.stroke()),ctx.restore()),ctx.shadowBlur=0,ctx.restore()}function gameOver(){gameRunning=!1,document.getElementById("finalScore").textContent=score,document.getElementById("gameOverScreen").style.display="block"}function gameLoop(){update(),draw(),requestAnimationFrame(gameLoop)}document.addEventListener("keydown",e=>{keys[e.code]=!0,"KeyP"===e.code&&gameRunning&&(gamePaused=!gamePaused),["Space","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.code)&&e.preventDefault()}),document.addEventListener("keyup",e=>{keys[e.code]=!1}),document.getElementById("startBtn").addEventListener("click",()=>{document.getElementById("startScreen").style.display="none",initGame(),gameRunning=!0}),document.getElementById("restartBtn").addEventListener("click",()=>{document.getElementById("gameOverScreen").style.display="none",initGame(),gameRunning=!0}),initStars(),gameLoop();</script>
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
76
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
77
|
+
</body></html>
|