@kosmas10/portal 0.0.4 → 0.0.6
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 +51 -55
- package/dist/portal.html +69 -0
- package/package.json +27 -8
- package/portal-bootstrap.html +51 -91
- package/portal.html +0 -460
package/portal-bootstrap.html
CHANGED
|
@@ -3,105 +3,65 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
6
|
+
<title>AI Chat Extensions Portal</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
+
<div id="root"></div>
|
|
9
10
|
<script>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Portal Bootstrap Loader
|
|
13
|
+
* Copyright (c) 2025 Nova Science Ventures LLC
|
|
14
|
+
* Created by Kosmas Karadimitriou
|
|
15
|
+
*
|
|
16
|
+
* Licensed under the MIT License
|
|
17
|
+
* GitHub: https://github.com/kosmas10/ai-chat-extensions
|
|
18
|
+
*
|
|
19
|
+
* Loads app from CDN and injects into current document (preserves Claude's fetch proxy)
|
|
20
|
+
*
|
|
21
|
+
* NOTE: Do NOT use registry.npmjs.org - Claude Artifacts blocks it.
|
|
22
|
+
* Always use cdn.jsdelivr.net for both version lookup and file fetch.
|
|
23
|
+
*/
|
|
24
|
+
(async function() {
|
|
25
|
+
var APP = '@kosmas10/portal';
|
|
26
|
+
var FILE = 'dist/portal.html';
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Fetches loader version from package.json to avoid @latest cache issues.
|
|
24
|
-
// Only change: the app package name (@kosmas10/portal)
|
|
25
|
-
// ============================================================================
|
|
26
|
-
(function() {
|
|
27
|
-
// Configuration: Only change this for different apps
|
|
28
|
-
var APP_PACKAGE = '@kosmas10/portal';
|
|
29
|
-
var APP_NAME = 'Portal';
|
|
28
|
+
try {
|
|
29
|
+
// Get latest version from CDN (NOT registry.npmjs.org - blocked in Claude Artifacts)
|
|
30
|
+
var pkg = await fetch('https://cdn.jsdelivr.net/npm/' + APP + '@latest/package.json?t=' + Date.now()).then(r => r.json());
|
|
31
|
+
var html = await fetch('https://cdn.jsdelivr.net/npm/' + APP + '@' + pkg.version + '/' + FILE).then(r => r.text());
|
|
30
32
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'h1{color:#c33;margin-top:0}' +
|
|
38
|
-
'code{background:#f5f5f5;padding:2px 6px;border-radius:3px;font-size:0.9em}' +
|
|
39
|
-
'ul{margin:15px 0}li{margin:8px 0}' +
|
|
40
|
-
'</style></head><body>' +
|
|
41
|
-
'<div class="error">' +
|
|
42
|
-
'<h1>Failed to Load Application</h1>' +
|
|
43
|
-
'<p>The application loader could not be loaded from:</p>' +
|
|
44
|
-
'<p><code>' + url + '</code></p>' +
|
|
45
|
-
'<p><strong>Possible causes:</strong></p>' +
|
|
46
|
-
'<ul>' +
|
|
47
|
-
'<li>No internet connection</li>' +
|
|
48
|
-
'<li>CDN (cdn.jsdelivr.net) is blocked by firewall or proxy</li>' +
|
|
49
|
-
'<li>NPM registry is inaccessible</li>' +
|
|
50
|
-
'<li>Corporate network restrictions</li>' +
|
|
51
|
-
'<li>Browser extensions blocking the request</li>' +
|
|
52
|
-
'</ul>' +
|
|
53
|
-
'<p><strong>What to try:</strong></p>' +
|
|
54
|
-
'<ul>' +
|
|
55
|
-
'<li>Check your internet connection</li>' +
|
|
56
|
-
'<li>Try accessing <a href="https://cdn.jsdelivr.net" target="_blank">cdn.jsdelivr.net</a> directly</li>' +
|
|
57
|
-
'<li>Disable VPN or proxy temporarily</li>' +
|
|
58
|
-
'<li>Check firewall settings</li>' +
|
|
59
|
-
'<li>Try a different network</li>' +
|
|
60
|
-
'<li>Contact your IT department if on a corporate network</li>' +
|
|
61
|
-
'</ul>' +
|
|
62
|
-
'</div></body></html>';
|
|
33
|
+
// Inject all styles
|
|
34
|
+
var styles = html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi);
|
|
35
|
+
for (var m of styles) {
|
|
36
|
+
var s = document.createElement('style');
|
|
37
|
+
s.textContent = m[1];
|
|
38
|
+
document.head.appendChild(s);
|
|
63
39
|
}
|
|
64
40
|
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
};
|
|
72
|
-
document.head.appendChild(script);
|
|
41
|
+
// Inject all scripts
|
|
42
|
+
var scripts = html.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/gi);
|
|
43
|
+
for (var m of scripts) {
|
|
44
|
+
var s = document.createElement('script');
|
|
45
|
+
s.textContent = m[1];
|
|
46
|
+
document.body.appendChild(s);
|
|
73
47
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
async function loadLoader() {
|
|
92
|
-
try {
|
|
93
|
-
var loaderVersion = await getLoaderVersion();
|
|
94
|
-
var loaderUrl = 'https://cdn.jsdelivr.net/npm/@kosmas10/html-app-loader@' + loaderVersion + '/loader.js?app=' + APP_PACKAGE;
|
|
95
|
-
loadScript(loaderUrl);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error(APP_NAME + ' Bootstrap: Error fetching loader version:', error);
|
|
98
|
-
var fallbackUrl = 'https://cdn.jsdelivr.net/npm/@kosmas10/html-app-loader@latest/loader.js?app=' + APP_PACKAGE;
|
|
99
|
-
loadScript(fallbackUrl);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
loadLoader();
|
|
104
|
-
})();
|
|
48
|
+
} catch (e) {
|
|
49
|
+
document.body.innerHTML = '<div style="max-width:600px;margin:40px auto;padding:30px;font-family:system-ui,sans-serif;background:#fff;border:2px solid #dc3545;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.1)">' +
|
|
50
|
+
'<h1 style="color:#dc3545;margin:0 0 15px 0;font-size:1.5em">⚠️ Unable to Load Portal</h1>' +
|
|
51
|
+
'<p style="background:#f8f9fa;padding:10px;border-radius:6px;font-family:monospace;font-size:0.9em;color:#666">' + e.message + '</p>' +
|
|
52
|
+
'<h2 style="font-size:1.1em;margin:20px 0 10px 0">What you can try:</h2>' +
|
|
53
|
+
'<ul style="margin:0;padding-left:20px;line-height:1.8">' +
|
|
54
|
+
'<li><strong>Refresh the page</strong> - Sometimes a simple refresh fixes temporary issues</li>' +
|
|
55
|
+
'<li><strong>Check your internet connection</strong> - The app needs to download from the internet</li>' +
|
|
56
|
+
'<li><strong>Wait a moment and try again</strong> - The server might be temporarily busy</li>' +
|
|
57
|
+
'<li><strong>Try a different browser</strong> - Some browser extensions can interfere</li>' +
|
|
58
|
+
'</ul>' +
|
|
59
|
+
'<p style="margin-top:20px;padding-top:15px;border-top:1px solid #eee;color:#666;font-size:0.9em">' +
|
|
60
|
+
'If the problem persists, please report it at: ' +
|
|
61
|
+
'<a href="https://github.com/kosmas10/ai-chat-extensions/issues" target="_blank" style="color:#0066cc">github.com/kosmas10/ai-chat-extensions</a>' +
|
|
62
|
+
'</p></div>';
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
105
65
|
</script>
|
|
106
66
|
</body>
|
|
107
67
|
</html>
|
package/portal.html
DELETED
|
@@ -1,460 +0,0 @@
|
|
|
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>AI Chat Extensions</title>
|
|
7
|
-
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect x='0' y='0' width='50' height='50' fill='%236ee7b7'/><rect x='50' y='0' width='50' height='50' fill='%23fbbf24'/><rect x='0' y='50' width='50' height='50' fill='%23764ba2'/><rect x='50' y='50' width='50' height='50' fill='%23ffffff'/></svg>">
|
|
8
|
-
<style>
|
|
9
|
-
* {
|
|
10
|
-
margin: 0;
|
|
11
|
-
padding: 0;
|
|
12
|
-
box-sizing: border-box;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
body {
|
|
16
|
-
font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
|
17
|
-
background: linear-gradient(135deg, #d4c4a8 0%, #c4b5a0 100%);
|
|
18
|
-
min-height: 100vh;
|
|
19
|
-
display: flex;
|
|
20
|
-
align-items: flex-start;
|
|
21
|
-
justify-content: center;
|
|
22
|
-
padding: 60px 20px 20px 20px;
|
|
23
|
-
overflow-x: hidden;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.container {
|
|
27
|
-
background: rgba(255, 255, 255, 0.95);
|
|
28
|
-
backdrop-filter: blur(10px);
|
|
29
|
-
border-radius: 20px;
|
|
30
|
-
padding: 40px 70px 70px 70px;
|
|
31
|
-
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
32
|
-
max-width: 871px;
|
|
33
|
-
width: 100%;
|
|
34
|
-
text-align: center;
|
|
35
|
-
box-sizing: border-box;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.version {
|
|
39
|
-
position: absolute;
|
|
40
|
-
top: 5px;
|
|
41
|
-
right: 10px;
|
|
42
|
-
font-size: 0.7rem;
|
|
43
|
-
opacity: 0.5;
|
|
44
|
-
color: #6b7280;
|
|
45
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
46
|
-
z-index: 1000;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.header {
|
|
50
|
-
margin-bottom: 40px;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.header h1 {
|
|
54
|
-
color: #333;
|
|
55
|
-
font-size: 2.5rem;
|
|
56
|
-
margin-bottom: 10px;
|
|
57
|
-
font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
|
58
|
-
background: linear-gradient(135deg, #000000, #333333);
|
|
59
|
-
-webkit-background-clip: text;
|
|
60
|
-
-webkit-text-fill-color: transparent;
|
|
61
|
-
background-clip: text;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.header p {
|
|
65
|
-
color: #666;
|
|
66
|
-
font-size: 1.1rem;
|
|
67
|
-
line-height: 1.6;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.apps-grid {
|
|
71
|
-
display: grid;
|
|
72
|
-
grid-template-columns: repeat(auto-fit, minmax(305px, 1fr));
|
|
73
|
-
gap: 30px;
|
|
74
|
-
margin-top: 40px;
|
|
75
|
-
width: 100%;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.app-card {
|
|
79
|
-
background: white;
|
|
80
|
-
border-radius: 15px;
|
|
81
|
-
padding: 30px;
|
|
82
|
-
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
83
|
-
transition: all 0.3s ease;
|
|
84
|
-
border: 2px solid transparent;
|
|
85
|
-
position: relative;
|
|
86
|
-
overflow: hidden;
|
|
87
|
-
box-sizing: border-box;
|
|
88
|
-
width: 100%;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.app-card::before {
|
|
92
|
-
content: '';
|
|
93
|
-
position: absolute;
|
|
94
|
-
top: 0;
|
|
95
|
-
left: 0;
|
|
96
|
-
right: 0;
|
|
97
|
-
height: 4px;
|
|
98
|
-
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/* Chat & Verify Card - Green Theme */
|
|
102
|
-
.chat-verify-card::before {
|
|
103
|
-
background: linear-gradient(135deg, #a7f3d0, #6ee7b7);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.chat-verify-card:hover {
|
|
107
|
-
border-color: #a7f3d0;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.chat-verify-button {
|
|
111
|
-
background: linear-gradient(135deg, #a7f3d0, #6ee7b7);
|
|
112
|
-
color: #1f2937;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.chat-verify-button:hover {
|
|
116
|
-
transform: translateY(-2px);
|
|
117
|
-
box-shadow: 0 10px 20px rgba(167, 243, 208, 0.3);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/* Folder Analyzer Card - Yellow Theme */
|
|
121
|
-
.folder-analyzer-card::before {
|
|
122
|
-
background: linear-gradient(135deg, #fde68a, #fbbf24);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.folder-analyzer-card:hover {
|
|
126
|
-
border-color: #fde68a;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.folder-analyzer-button {
|
|
130
|
-
background: linear-gradient(135deg, #fde68a, #fbbf24);
|
|
131
|
-
color: #1f2937;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.folder-analyzer-button:hover {
|
|
135
|
-
transform: translateY(-2px);
|
|
136
|
-
box-shadow: 0 10px 20px rgba(253, 230, 138, 0.3);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.app-card:hover {
|
|
140
|
-
transform: translateY(-5px);
|
|
141
|
-
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/* Chat & Verify hover effects */
|
|
145
|
-
.chat-verify-card:hover {
|
|
146
|
-
border-color: #d1d5db;
|
|
147
|
-
box-shadow: 0 20px 40px rgba(209, 213, 219, 0.2);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/* Folder Analyzer hover effects */
|
|
151
|
-
.folder-analyzer-card:hover {
|
|
152
|
-
border-color: #d1d5db;
|
|
153
|
-
box-shadow: 0 20px 40px rgba(209, 213, 219, 0.2);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.app-icon {
|
|
157
|
-
width: 60px;
|
|
158
|
-
height: 60px;
|
|
159
|
-
margin: 0 auto 20px;
|
|
160
|
-
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
161
|
-
border-radius: 50%;
|
|
162
|
-
display: flex;
|
|
163
|
-
align-items: center;
|
|
164
|
-
justify-content: center;
|
|
165
|
-
font-size: 24px;
|
|
166
|
-
color: white;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/* Chat & Verify Icon - Green Theme */
|
|
170
|
-
.chat-verify-card .app-icon {
|
|
171
|
-
background: linear-gradient(135deg, #a7f3d0, #6ee7b7);
|
|
172
|
-
color: #1f2937;
|
|
173
|
-
position: relative;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/* Custom thick checkmark for Chat & Verify */
|
|
177
|
-
.chat-verify-card .app-icon::before {
|
|
178
|
-
content: '';
|
|
179
|
-
position: absolute;
|
|
180
|
-
width: 8px;
|
|
181
|
-
height: 16px;
|
|
182
|
-
border: solid #1f2937;
|
|
183
|
-
border-width: 0 4px 4px 0;
|
|
184
|
-
transform: rotate(45deg);
|
|
185
|
-
margin-top: -2px;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/* Folder Analyzer Icon - Yellow Theme */
|
|
189
|
-
.folder-analyzer-card .app-icon {
|
|
190
|
-
background: linear-gradient(135deg, #fde68a, #fbbf24);
|
|
191
|
-
color: #1f2937;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.app-title {
|
|
195
|
-
font-size: 1.5rem;
|
|
196
|
-
color: #333;
|
|
197
|
-
margin-bottom: 15px;
|
|
198
|
-
font-weight: 600;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.app-description {
|
|
202
|
-
color: #666;
|
|
203
|
-
line-height: 1.6;
|
|
204
|
-
margin-bottom: 25px;
|
|
205
|
-
font-size: 0.95rem;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.app-features {
|
|
209
|
-
list-style: none;
|
|
210
|
-
margin-bottom: 25px;
|
|
211
|
-
text-align: left;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.app-features li {
|
|
215
|
-
color: #555;
|
|
216
|
-
margin-bottom: 8px;
|
|
217
|
-
padding-left: 20px;
|
|
218
|
-
position: relative;
|
|
219
|
-
font-size: 0.9rem;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.app-features li::before {
|
|
223
|
-
content: '✓';
|
|
224
|
-
position: absolute;
|
|
225
|
-
left: 0;
|
|
226
|
-
color: #667eea;
|
|
227
|
-
font-weight: bold;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.app-button {
|
|
231
|
-
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
232
|
-
color: white;
|
|
233
|
-
border: none;
|
|
234
|
-
padding: 12px 30px;
|
|
235
|
-
border-radius: 25px;
|
|
236
|
-
font-size: 1rem;
|
|
237
|
-
font-weight: 600;
|
|
238
|
-
cursor: pointer;
|
|
239
|
-
transition: all 0.3s ease;
|
|
240
|
-
text-decoration: none;
|
|
241
|
-
display: inline-block;
|
|
242
|
-
width: 100%;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.app-button:hover {
|
|
246
|
-
transform: translateY(-2px);
|
|
247
|
-
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/* Themed button styles */
|
|
251
|
-
.chat-verify-button {
|
|
252
|
-
background: linear-gradient(135deg, #a7f3d0, #6ee7b7);
|
|
253
|
-
color: #1f2937;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.chat-verify-button:hover {
|
|
257
|
-
transform: translateY(-2px);
|
|
258
|
-
box-shadow: 0 10px 20px rgba(167, 243, 208, 0.3);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.folder-analyzer-button {
|
|
262
|
-
background: linear-gradient(135deg, #fde68a, #fbbf24);
|
|
263
|
-
color: #1f2937;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.folder-analyzer-button:hover {
|
|
267
|
-
transform: translateY(-2px);
|
|
268
|
-
box-shadow: 0 10px 20px rgba(253, 230, 138, 0.3);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.footer {
|
|
272
|
-
margin-top: 40px;
|
|
273
|
-
padding-top: 20px;
|
|
274
|
-
border-top: 1px solid #eee;
|
|
275
|
-
color: #666;
|
|
276
|
-
font-size: 0.9rem;
|
|
277
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
@media (max-width: 768px) {
|
|
281
|
-
body {
|
|
282
|
-
padding: 40px 10px 10px 10px;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
.container {
|
|
286
|
-
padding: 20px;
|
|
287
|
-
margin: 0;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.header h1 {
|
|
291
|
-
font-size: 2rem;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.apps-grid {
|
|
295
|
-
grid-template-columns: 1fr;
|
|
296
|
-
gap: 20px;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.app-card {
|
|
300
|
-
padding: 20px;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
@media (max-width: 480px) {
|
|
305
|
-
body {
|
|
306
|
-
padding: 20px 5px 5px 5px;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.container {
|
|
310
|
-
padding: 15px;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
.header h1 {
|
|
314
|
-
font-size: 1.8rem;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.app-card {
|
|
318
|
-
padding: 15px;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
</style>
|
|
322
|
-
</head>
|
|
323
|
-
<body>
|
|
324
|
-
<div class="version">v<span id="app-version"></span></div>
|
|
325
|
-
|
|
326
|
-
<div class="container">
|
|
327
|
-
<div class="header">
|
|
328
|
-
<h1>AI Chat Extensions</h1>
|
|
329
|
-
<p>Tools and extensions to enhance AI chat productivity.</p>
|
|
330
|
-
</div>
|
|
331
|
-
|
|
332
|
-
<div class="apps-grid">
|
|
333
|
-
<div class="app-card chat-verify-card">
|
|
334
|
-
<div class="app-icon"></div>
|
|
335
|
-
<h2 class="app-title">Chat & Verify</h2>
|
|
336
|
-
<p class="app-description">
|
|
337
|
-
Chat with citations and highlights in the source document for easy verifications.
|
|
338
|
-
</p>
|
|
339
|
-
<button class="app-button chat-verify-button" onclick="openApp('chat-and-verify')">
|
|
340
|
-
Launch Chat & Verify
|
|
341
|
-
</button>
|
|
342
|
-
</div>
|
|
343
|
-
|
|
344
|
-
<div class="app-card folder-analyzer-card">
|
|
345
|
-
<div class="app-icon">📁</div>
|
|
346
|
-
<h2 class="app-title">Folder LLM Analyzer</h2>
|
|
347
|
-
<p class="app-description">
|
|
348
|
-
Runs the same LLM query on each file in a folder and displays results in a grid.
|
|
349
|
-
</p>
|
|
350
|
-
<button class="app-button folder-analyzer-button" onclick="openApp('folder-llm-analyzer')">
|
|
351
|
-
Launch Folder Analyzer
|
|
352
|
-
</button>
|
|
353
|
-
</div>
|
|
354
|
-
</div>
|
|
355
|
-
|
|
356
|
-
</div>
|
|
357
|
-
|
|
358
|
-
<script>
|
|
359
|
-
// App version - keep in sync with package.json
|
|
360
|
-
const APP_VERSION = '0.0.4';
|
|
361
|
-
|
|
362
|
-
// CDN base URL for loading apps
|
|
363
|
-
const CDN_BASE = 'https://cdn.jsdelivr.net/npm/@kosmas10/';
|
|
364
|
-
|
|
365
|
-
// Cache for resolved versions to avoid multiple fetches
|
|
366
|
-
const versionCache = {};
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Fetch the latest version from package.json
|
|
370
|
-
* This avoids @latest cache issues by fetching package.json (which updates faster)
|
|
371
|
-
* and then using the specific version number
|
|
372
|
-
*/
|
|
373
|
-
async function getLatestVersion(packageName) {
|
|
374
|
-
// Check cache first
|
|
375
|
-
if (versionCache[packageName]) {
|
|
376
|
-
return versionCache[packageName];
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
try {
|
|
380
|
-
const cacheBuster = Date.now();
|
|
381
|
-
const packageJsonUrl = CDN_BASE + packageName + '@latest/package.json?t=' + cacheBuster;
|
|
382
|
-
|
|
383
|
-
const response = await fetch(packageJsonUrl);
|
|
384
|
-
if (!response.ok) {
|
|
385
|
-
throw new Error('Failed to fetch package.json: HTTP ' + response.status);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const packageData = await response.json();
|
|
389
|
-
if (!packageData.version) {
|
|
390
|
-
throw new Error('package.json does not contain version field');
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Cache the version
|
|
394
|
-
versionCache[packageName] = packageData.version;
|
|
395
|
-
return packageData.version;
|
|
396
|
-
} catch (error) {
|
|
397
|
-
console.error('Portal: Error fetching latest version for', packageName, ':', error);
|
|
398
|
-
// Fallback to 'latest' if we can't fetch the version
|
|
399
|
-
return 'latest';
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async function openApp(packageName) {
|
|
404
|
-
try {
|
|
405
|
-
// Resolve 'latest' to actual version number
|
|
406
|
-
const resolvedVersion = await getLatestVersion(packageName);
|
|
407
|
-
|
|
408
|
-
// Build URL with specific version (not @latest)
|
|
409
|
-
const url = CDN_BASE + packageName + '@' + resolvedVersion + '/' + packageName + '-bootstrap.html';
|
|
410
|
-
|
|
411
|
-
// Open the application bootstrap from CDN in a new tab/window
|
|
412
|
-
window.open(url, '_blank');
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error('Portal: Error opening app', packageName, ':', error);
|
|
415
|
-
// Fallback: try with @latest if version resolution fails
|
|
416
|
-
const fallbackUrl = CDN_BASE + packageName + '@latest/' + packageName + '-bootstrap.html';
|
|
417
|
-
window.open(fallbackUrl, '_blank');
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Add some interactive effects
|
|
422
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
423
|
-
// Set the version display from the constant
|
|
424
|
-
const versionElement = document.getElementById('app-version');
|
|
425
|
-
if (versionElement) {
|
|
426
|
-
versionElement.textContent = APP_VERSION;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const cards = document.querySelectorAll('.app-card');
|
|
430
|
-
|
|
431
|
-
cards.forEach(card => {
|
|
432
|
-
card.addEventListener('mouseenter', function() {
|
|
433
|
-
this.style.transform = 'translateY(-5px) scale(1.02)';
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
card.addEventListener('mouseleave', function() {
|
|
437
|
-
this.style.transform = 'translateY(0) scale(1)';
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// Make entire card clickable
|
|
441
|
-
card.addEventListener('click', function(e) {
|
|
442
|
-
// Don't trigger if clicking on the button (to avoid double-triggering)
|
|
443
|
-
if (e.target.classList.contains('app-button')) {
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Find the button in this card and trigger its click
|
|
448
|
-
const button = this.querySelector('.app-button');
|
|
449
|
-
if (button) {
|
|
450
|
-
button.click();
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
// Add cursor pointer to indicate clickability
|
|
455
|
-
card.style.cursor = 'pointer';
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
</script>
|
|
459
|
-
</body>
|
|
460
|
-
</html>
|