@saltcorn/bootstrap-prompt-theme 0.1.3 → 0.1.5
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/agent-skill.js +63 -90
- package/index.js +114 -0
- package/package.json +1 -1
package/agent-skill.js
CHANGED
|
@@ -55,11 +55,12 @@ CRITICAL RULES — never break these:
|
|
|
55
55
|
- Never set z-index on .card or page content elements — stacking context on page content is what causes navbar dropdowns to be obscured.
|
|
56
56
|
- Do not override --bs-zindex-dropdown, --bs-zindex-fixed, or .dropdown-menu z-index.
|
|
57
57
|
- Dropdowns must always be fully visible and on top of all other page content, including cards, sections, and containers.
|
|
58
|
+
- Always pair background and foreground colors: whenever you set a background color on any element — form controls (.form-control, .form-select), dropdowns (.dropdown-menu, .dropdown-item), navbar (.navbar, .nav-link), cards, or any container — you must also explicitly set a readable text/foreground color on that element and its direct text children. Bootstrap does not reliably propagate text color into all components, especially in dark mode. Common failure cases: dark .dropdown-menu background with light-mode .dropdown-item text color (text disappears); dark .form-control background with inherited body text color (typed text invisible); dark navbar with unset .nav-link color. Always set both background and text color together. When in doubt, also set the matching --bs-* token pair (e.g. --bs-dropdown-bg with --bs-dropdown-link-color, --bs-body-bg with --bs-body-color).
|
|
58
59
|
|
|
59
60
|
IMAGES: The user may attach images to the conversation. Use them as design inspiration — extract colors, typography style, spacing feel, or overall mood and translate that into the CSS overlay. If the user attaches an image without further instruction, derive a theme from it. If they describe what they want alongside the image, use the image to inform the details.
|
|
60
61
|
|
|
61
62
|
LAYOUT CONFIG: Besides CSS, you can control structural layout options via set_layout_config:
|
|
62
|
-
- mode: "light" | "dark" — Bootstrap color mode applied to <html data-bs-theme
|
|
63
|
+
- mode: "light" | "dark" — Bootstrap color mode applied to <html data-bs-theme>. IMPORTANT: whenever the user asks for a dark theme or dark mode, you MUST call set_layout_config with mode: "dark". Do not only adjust CSS colors — without mode: "dark" Bootstrap's own dark-mode component styles will not activate and the result will look wrong.
|
|
63
64
|
- menu_style: "Top Navbar" | "Side Navbar" | "No Menu"
|
|
64
65
|
- colorscheme: navbar color class pair, e.g. "navbar-dark bg-dark", "navbar-light bg-light", "navbar-dark bg-primary"
|
|
65
66
|
- fixed_top: true | false — fix navbar to top of viewport
|
|
@@ -67,78 +68,9 @@ LAYOUT CONFIG: Besides CSS, you can control structural layout options via set_la
|
|
|
67
68
|
- top_pad: 0–5 — Bootstrap spacing scale for top padding on page sections
|
|
68
69
|
- in_card: true | false — wrap page body in a Bootstrap card
|
|
69
70
|
Only call set_layout_config when you want to change structural layout, separate from CSS. Call both tools when a request requires both structural and CSS changes.
|
|
71
|
+
When you change menu_style, the tool response will include a page_structure field with rendered HTML of the new layout. Read it to understand the actual element hierarchy and CSS selectors before writing any CSS for that layout.
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
\`\`\`html
|
|
73
|
-
<html lang="en" data-bs-theme="light">
|
|
74
|
-
<head>
|
|
75
|
-
<meta charset="utf-8">
|
|
76
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
77
|
-
<link href="/plugins/public/bootstrap-prompt-theme/bootstrap.min.css" rel="stylesheet">
|
|
78
|
-
<link rel="stylesheet" href="/plugins/public/bootstrap-prompt-theme/sidebar-3.css">
|
|
79
|
-
<link rel="stylesheet" href="/plugins/public/bootstrap-prompt-theme/overlay.css">
|
|
80
|
-
<link href="/static_assets/.../saltcorn.css" rel="stylesheet">
|
|
81
|
-
<script>var _sc_globalCsrf = "<csrf-token>", _sc_version_tag = "...", _sc_lightmode = "light";</script>
|
|
82
|
-
</head>
|
|
83
|
-
<body id="page-top" class="page_<pagename>">
|
|
84
|
-
<div id="wrapper">
|
|
85
|
-
<nav class="navbar d-print-none navbar-expand-md navbar-light bg-light" id="mainNav">
|
|
86
|
-
<div class="container">
|
|
87
|
-
<a class="navbar-brand" href="/">Saltcorn</a>
|
|
88
|
-
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive">
|
|
89
|
-
<span class="navbar-toggler-icon"></span>
|
|
90
|
-
</button>
|
|
91
|
-
<div class="collapse navbar-collapse" id="navbarResponsive">
|
|
92
|
-
<ul class="navbar-nav ms-auto my-2 my-lg-0">
|
|
93
|
-
<li class="nav-item"><a class="nav-link" href="/table">Tables</a></li>
|
|
94
|
-
<li class="nav-item dropdown">
|
|
95
|
-
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Settings</a>
|
|
96
|
-
<div class="dropdown-menu">
|
|
97
|
-
<a class="dropdown-item" href="/admin">About application</a>
|
|
98
|
-
<a class="dropdown-item" href="/plugins">Modules</a>
|
|
99
|
-
</div>
|
|
100
|
-
</li>
|
|
101
|
-
<li class="nav-item dropdown">
|
|
102
|
-
<a class="nav-link dropdown-toggle user-nav-section" href="#" data-bs-toggle="dropdown">User</a>
|
|
103
|
-
<div class="dropdown-menu dropdown-menu-end">
|
|
104
|
-
<a class="dropdown-item" href="/auth/settings">User settings</a>
|
|
105
|
-
<a class="dropdown-item" href="/auth/logout">Logout</a>
|
|
106
|
-
</div>
|
|
107
|
-
</li>
|
|
108
|
-
</ul>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
</nav>
|
|
112
|
-
<div id="page-inner-content">
|
|
113
|
-
<section class="page-section pt-2">
|
|
114
|
-
<div class="container">
|
|
115
|
-
<!-- admin edit bar (admin only) -->
|
|
116
|
-
<div class="card p-1 mt-1 mb-3 d-print-none admin-edit-bar">
|
|
117
|
-
<div class="card-body p-1">...</div>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</section>
|
|
121
|
-
<section class="page-section">
|
|
122
|
-
<div class="container">
|
|
123
|
-
<!-- page content, e.g. a table view -->
|
|
124
|
-
<div class="table-responsive">
|
|
125
|
-
<table class="table table-sm table-valign-middle">
|
|
126
|
-
<thead><tr><th>Email</th><th>Role</th></tr></thead>
|
|
127
|
-
<tbody><tr><td>admin@foo.com</td><td>1</td></tr></tbody>
|
|
128
|
-
</table>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
</section>
|
|
132
|
-
<section class="page-section">
|
|
133
|
-
<div class="container">
|
|
134
|
-
<div id="toasts-area" class="toast-container position-fixed top-0 end-0 p-2" style="z-index: 9999;"></div>
|
|
135
|
-
</div>
|
|
136
|
-
</section>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
</body>
|
|
140
|
-
</html>
|
|
141
|
-
\`\`\`
|
|
73
|
+
{{PAGE_STRUCTURE}}
|
|
142
74
|
|
|
143
75
|
WORKFLOW:
|
|
144
76
|
1. Call apply_css_overlay with the complete CSS — this is the only way to deliver CSS.
|
|
@@ -203,6 +135,18 @@ const savePluginConfig = async (plugin, patch) => {
|
|
|
203
135
|
});
|
|
204
136
|
};
|
|
205
137
|
|
|
138
|
+
const renderStructureSkeleton = (config) => {
|
|
139
|
+
try {
|
|
140
|
+
return require(join(__dirname, "index.js")).renderStructureSkeleton(config);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
getState().log(
|
|
143
|
+
4,
|
|
144
|
+
`bootstrap-prompt-theme: renderStructureSkeleton failed: ${e.message}`
|
|
145
|
+
);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
206
150
|
class GenerateBootstrapThemeSkill {
|
|
207
151
|
static skill_name = "Generate Bootstrap Theme";
|
|
208
152
|
|
|
@@ -218,8 +162,26 @@ class GenerateBootstrapThemeSkill {
|
|
|
218
162
|
return [];
|
|
219
163
|
}
|
|
220
164
|
|
|
221
|
-
systemPrompt() {
|
|
222
|
-
|
|
165
|
+
async systemPrompt() {
|
|
166
|
+
const plugin = await findPlugin();
|
|
167
|
+
const config = plugin?.configuration || {};
|
|
168
|
+
let pageStructure =
|
|
169
|
+
"PAGE STRUCTURE: (unavailable — could not render current layout)";
|
|
170
|
+
try {
|
|
171
|
+
const { renderPageSkeleton } = require(join(__dirname, "index.js"));
|
|
172
|
+
const html = renderPageSkeleton(config);
|
|
173
|
+
if (html)
|
|
174
|
+
pageStructure =
|
|
175
|
+
"PAGE STRUCTURE: The current page renders like this (with a sample list view, based on the active layout config). Use it to understand element hierarchy, class names, and selectors when writing CSS:\n```html\n" +
|
|
176
|
+
html +
|
|
177
|
+
"\n```";
|
|
178
|
+
} catch (e) {
|
|
179
|
+
getState().log(
|
|
180
|
+
4,
|
|
181
|
+
`bootstrap-prompt-theme: systemPrompt renderPageSkeleton failed: ${e.message}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return SYSTEM_PROMPT.replace("{{PAGE_STRUCTURE}}", pageStructure);
|
|
223
185
|
}
|
|
224
186
|
|
|
225
187
|
provideTools() {
|
|
@@ -281,30 +243,41 @@ class GenerateBootstrapThemeSkill {
|
|
|
281
243
|
return pre(code(entries));
|
|
282
244
|
},
|
|
283
245
|
process: async (params) => {
|
|
246
|
+
const allowed = [
|
|
247
|
+
"mode",
|
|
248
|
+
"menu_style",
|
|
249
|
+
"colorscheme",
|
|
250
|
+
"fixed_top",
|
|
251
|
+
"fluid",
|
|
252
|
+
"top_pad",
|
|
253
|
+
"in_card",
|
|
254
|
+
];
|
|
255
|
+
const patch = Object.fromEntries(
|
|
256
|
+
Object.entries(params).filter(([k]) => allowed.includes(k))
|
|
257
|
+
);
|
|
284
258
|
const plugin = await findPlugin();
|
|
285
259
|
if (plugin) {
|
|
286
|
-
const allowed = [
|
|
287
|
-
"mode",
|
|
288
|
-
"menu_style",
|
|
289
|
-
"colorscheme",
|
|
290
|
-
"fixed_top",
|
|
291
|
-
"fluid",
|
|
292
|
-
"top_pad",
|
|
293
|
-
"in_card",
|
|
294
|
-
];
|
|
295
|
-
const patch = Object.fromEntries(
|
|
296
|
-
Object.entries(params).filter(([k]) => allowed.includes(k))
|
|
297
|
-
);
|
|
298
260
|
await savePluginConfig(plugin, patch);
|
|
299
261
|
}
|
|
300
|
-
|
|
262
|
+
const result = { success: true };
|
|
263
|
+
if (params.menu_style) {
|
|
264
|
+
const mergedConfig = { ...(plugin?.configuration || {}), ...patch };
|
|
265
|
+
result.page_structure = renderStructureSkeleton(mergedConfig);
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
301
268
|
},
|
|
302
|
-
renderToolResponse: async () =>
|
|
303
|
-
div(
|
|
269
|
+
renderToolResponse: async ({ page_structure }) =>
|
|
270
|
+
div(
|
|
271
|
+
{ class: "text-success" },
|
|
272
|
+
"Layout configuration updated.",
|
|
273
|
+
page_structure
|
|
274
|
+
? pre({ class: "mt-2 small" }, code(page_structure))
|
|
275
|
+
: ""
|
|
276
|
+
),
|
|
304
277
|
function: {
|
|
305
278
|
name: "set_layout_config",
|
|
306
279
|
description:
|
|
307
|
-
"Set structural layout configuration for the Saltcorn UI. Only pass the parameters you want to change.",
|
|
280
|
+
"Set structural layout configuration for the Saltcorn UI. Only pass the parameters you want to change. When menu_style is set, the tool response includes a page_structure field containing rendered HTML of the new layout — use it to understand the correct selectors before writing CSS.",
|
|
308
281
|
parameters: {
|
|
309
282
|
type: "object",
|
|
310
283
|
properties: {
|
package/index.js
CHANGED
|
@@ -928,6 +928,118 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
928
928
|
],
|
|
929
929
|
});
|
|
930
930
|
|
|
931
|
+
const FAKE_MENU = [
|
|
932
|
+
{
|
|
933
|
+
section: "Main",
|
|
934
|
+
items: [
|
|
935
|
+
{ label: "Tables", link: "/table" },
|
|
936
|
+
{ label: "Views", link: "/viewedit" },
|
|
937
|
+
{ label: "Pages", link: "/pageedit" },
|
|
938
|
+
{
|
|
939
|
+
label: "Settings",
|
|
940
|
+
subitems: [
|
|
941
|
+
{ label: "About application", link: "/admin" },
|
|
942
|
+
{ label: "Modules", link: "/plugins" },
|
|
943
|
+
{ label: "Users and security", link: "/useradmin" },
|
|
944
|
+
],
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
label: "User",
|
|
948
|
+
subitems: [
|
|
949
|
+
{ label: "User settings", link: "/auth/settings" },
|
|
950
|
+
{ label: "Logout", link: "/auth/logout" },
|
|
951
|
+
],
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
},
|
|
955
|
+
];
|
|
956
|
+
|
|
957
|
+
const FAKE_LIST_BODY = `
|
|
958
|
+
<section class="page-section pt-2"><div class="container">
|
|
959
|
+
<div class="card p-1 mt-1 mb-3 d-print-none admin-edit-bar"><div class="card-body p-1">
|
|
960
|
+
<i class="fas fa-user-cog me-1"></i>
|
|
961
|
+
<span class="ms-1 me-2 badge bg-secondary">Page</span>
|
|
962
|
+
<span class="copy-to-clipboard">books_with_edit</span>
|
|
963
|
+
<a class="ms-2" href="/pageedit/edit/books_with_edit">Edit <i class="fas fa-edit"></i></a>
|
|
964
|
+
</div></div>
|
|
965
|
+
</div></section>
|
|
966
|
+
<section class="page-section"><div class="container">
|
|
967
|
+
<div class="d-inline" data-sc-embed-viewname="my_bread">
|
|
968
|
+
<div class="container"><nav aria-label="breadcrumb"><ol class="breadcrumb">
|
|
969
|
+
<li class="breadcrumb-item"><a href="/settings">Settings</a></li>
|
|
970
|
+
<li class="breadcrumb-item"><a href="/admin">About application</a></li>
|
|
971
|
+
<li class="breadcrumb-item active" aria-current="page">Site identity</li>
|
|
972
|
+
</ol></nav></div>
|
|
973
|
+
</div>
|
|
974
|
+
</div></section>
|
|
975
|
+
<section class="page-section"><div class="container">
|
|
976
|
+
<div class="d-inline" data-sc-embed-viewname="books">
|
|
977
|
+
<div class="table-responsive"><table class="table table-sm table-valign-middle table-hover">
|
|
978
|
+
<thead><tr>
|
|
979
|
+
<th><span class="link-style">name</span></th>
|
|
980
|
+
<th><span class="link-style">open</span></th>
|
|
981
|
+
<th class="text-align-right"><span class="link-style">pages</span></th>
|
|
982
|
+
</tr></thead>
|
|
983
|
+
<tbody>
|
|
984
|
+
<tr data-row-id="2">
|
|
985
|
+
<td>War and Peace</td>
|
|
986
|
+
<td><i class="fas fa-lg fa-times-circle text-danger"></i></td>
|
|
987
|
+
<td class="text-align-right">1000</td>
|
|
988
|
+
</tr>
|
|
989
|
+
</tbody>
|
|
990
|
+
</table></div>
|
|
991
|
+
</div>
|
|
992
|
+
</div></section>
|
|
993
|
+
<section class="page-section"><div class="container">
|
|
994
|
+
<div class="d-inline" data-sc-embed-viewname="edit_book">
|
|
995
|
+
<form data-viewname="edit_book" action="/view/edit_book" class="form-namespace" method="post">
|
|
996
|
+
<input type="hidden" name="_csrf" value="<csrf-token>">
|
|
997
|
+
<input type="hidden" class="form-control" name="id" value="2">
|
|
998
|
+
<span style="margin-bottom:1.5rem"><div class="row" style="margin-bottom:1.5rem">
|
|
999
|
+
<div class="col-md-2 text-start text-lg-end"><label for="inputname">name</label></div>
|
|
1000
|
+
<div class="col-md-10 text-start"><input type="text" class="form-control" data-fieldname="name" name="name" id="inputname" value="War and Peace"></div>
|
|
1001
|
+
</div></span>
|
|
1002
|
+
<span style="margin-bottom:1.5rem"><div class="row" style="margin-bottom:1.5rem">
|
|
1003
|
+
<div class="col-md-2 text-start text-lg-end"><label for="inputopen">open</label></div>
|
|
1004
|
+
<div class="col-md-10 text-start"><input class="me-2 mt-1" data-fieldname="open" type="checkbox" name="open" id="inputopen"></div>
|
|
1005
|
+
</div></span>
|
|
1006
|
+
<span style="margin-bottom:1.5rem"><div class="row" style="margin-bottom:1.5rem">
|
|
1007
|
+
<div class="col-md-2 text-start text-lg-end"><label for="inputpages">pages</label></div>
|
|
1008
|
+
<div class="col-md-10 text-start"><input type="number" class="form-control" data-fieldname="pages" name="pages" id="inputpages" step="1" value="1000"></div>
|
|
1009
|
+
</div></span>
|
|
1010
|
+
<span style="margin-bottom:1.5rem"><div class="row" style="margin-bottom:1.5rem">
|
|
1011
|
+
<div class="col-2 text-end"></div>
|
|
1012
|
+
<div class="col-10 text-start"><button type="submit" class="btn btn-primary">Save</button></div>
|
|
1013
|
+
</div></span>
|
|
1014
|
+
</form>
|
|
1015
|
+
</div>
|
|
1016
|
+
</div></section>
|
|
1017
|
+
<section class="page-section"><div class="container">
|
|
1018
|
+
<div id="toasts-area" class="toast-container position-fixed top-0 end-0 p-2" style="z-index:9999;" aria-live="polite" aria-atomic="true"></div>
|
|
1019
|
+
</div></section>`;
|
|
1020
|
+
|
|
1021
|
+
const renderStructureSkeleton = (config) =>
|
|
1022
|
+
menuWrap({
|
|
1023
|
+
brand: { name: "Brand" },
|
|
1024
|
+
menu: FAKE_MENU,
|
|
1025
|
+
config,
|
|
1026
|
+
currentUrl: "/",
|
|
1027
|
+
originalUrl: "/",
|
|
1028
|
+
body: '<section class="page-section pt-2"><div class="container"><p><!-- page content --></p></div></section>',
|
|
1029
|
+
req: null,
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
const renderPageSkeleton = (config) =>
|
|
1033
|
+
menuWrap({
|
|
1034
|
+
brand: { name: "Brand" },
|
|
1035
|
+
menu: FAKE_MENU,
|
|
1036
|
+
config,
|
|
1037
|
+
currentUrl: "/",
|
|
1038
|
+
originalUrl: "/",
|
|
1039
|
+
body: FAKE_LIST_BODY,
|
|
1040
|
+
req: null,
|
|
1041
|
+
});
|
|
1042
|
+
|
|
931
1043
|
module.exports = {
|
|
932
1044
|
sc_plugin_api_version: 1,
|
|
933
1045
|
plugin_name: "bootstrap-prompt-theme",
|
|
@@ -980,4 +1092,6 @@ module.exports = {
|
|
|
980
1092
|
}
|
|
981
1093
|
},
|
|
982
1094
|
ready_for_mobile: true,
|
|
1095
|
+
renderStructureSkeleton,
|
|
1096
|
+
renderPageSkeleton,
|
|
983
1097
|
};
|