workpeg 0.94.3__tar.gz → 0.95.0__tar.gz
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.
- {workpeg-0.94.3/src/workpeg.egg-info → workpeg-0.95.0}/PKG-INFO +1 -1
- {workpeg-0.94.3 → workpeg-0.95.0}/pyproject.toml +1 -1
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/runtime/scaffold.js +33 -4
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/runtime/workpeg-runtime.js +44 -0
- {workpeg-0.94.3 → workpeg-0.95.0/src/workpeg.egg-info}/PKG-INFO +1 -1
- {workpeg-0.94.3 → workpeg-0.95.0}/LICENSE +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/MANIFEST.in +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/README.md +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/setup.cfg +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/build.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/cdn.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/cli.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/config.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/context.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/create_new.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/run.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/runtime.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/submit.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/Dockerfile +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/LICENSE +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/README.md +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/app/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/app/main.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/templates/functions/requirements.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/actions.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/app.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/build.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/compiler/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/compiler/ast_parser.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/compiler/ir.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/compiler/js_generator.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/create.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/fake_cloud.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/minify.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/runtime/ai.js +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/runtime/pegment.css +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/scaffold.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/serve.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/state.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/validators.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/watcher.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/__init__.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/base.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/canvas.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/display.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/forms.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/futures.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/gestures.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/interactive.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/ui/widgets/layout.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg/utils.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg.egg-info/SOURCES.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg.egg-info/dependency_links.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg.egg-info/entry_points.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg.egg-info/requires.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/src/workpeg.egg-info/top_level.txt +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_build.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_cdn.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_changelog.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_changelog_guard.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_cli.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_config.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_context.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_create_new.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_release_script.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_run.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_runtime.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_submit.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_utils.py +0 -0
- {workpeg-0.94.3 → workpeg-0.95.0}/tests/test_watch_pipeline.py +0 -0
|
@@ -308,8 +308,8 @@
|
|
|
308
308
|
|
|
309
309
|
// A built-in rail row (Search / Workpeg AI) — the same look as a host
|
|
310
310
|
// NavItem: icon + fading label, the collapse re-centre via the padding var.
|
|
311
|
-
function navRow(iconName, label, href) {
|
|
312
|
-
return el("a", "wps-nav-row", {
|
|
311
|
+
function navRow(iconName, label, href, extraClass) {
|
|
312
|
+
return el("a", "wps-nav-row" + (extraClass ? " " + extraClass : ""), {
|
|
313
313
|
attrs: { href: href, title: label },
|
|
314
314
|
children: [
|
|
315
315
|
icon(iconName, 18),
|
|
@@ -564,6 +564,16 @@
|
|
|
564
564
|
"display:var(--wp-topnav-chat, none);align-items:center;"
|
|
565
565
|
+ "font-size:16px;font-weight:600;color:var(--color-text-primary)";
|
|
566
566
|
|
|
567
|
+
// "New chat" — only on an open conversation (/chats/<id>), a fresh-compose
|
|
568
|
+
// shortcut that starts a new chat without hunting for the sidebar row.
|
|
569
|
+
// Gated by STYLES on .pg-chat-route.wpai-chat-open (the same classes the
|
|
570
|
+
// runtime + ai.js already flip), so a scaffold.js CDN update delivers it
|
|
571
|
+
// out of band — no pegment var, no rebuild.
|
|
572
|
+
var newChat = el("div", "wps-newchat", { children: [
|
|
573
|
+
topAction("edit", "New chat", function () {
|
|
574
|
+
navigate(bizHref("/chats/new"));
|
|
575
|
+
})] });
|
|
576
|
+
|
|
567
577
|
var history = el("div", "", { children: [
|
|
568
578
|
topAction("history", "Chat history", function () {
|
|
569
579
|
try { WP().toggleAIHistory(); } catch (e) { /* no runtime */ }
|
|
@@ -590,7 +600,7 @@
|
|
|
590
600
|
el("div", "wps-topnav-row", { children: [
|
|
591
601
|
el("div", "wps-topnav-left", { children: [hostWrap, chatTitle] }),
|
|
592
602
|
el("div", "wps-topnav-actions", { children: [
|
|
593
|
-
history, spy, appsBtn, ai] })
|
|
603
|
+
newChat, history, spy, appsBtn, ai] })
|
|
594
604
|
] })
|
|
595
605
|
] });
|
|
596
606
|
// Inline padding (not stylesheet) so pegment's .wpai-* !important
|
|
@@ -625,7 +635,19 @@
|
|
|
625
635
|
// screens.
|
|
626
636
|
var mid = el("div", "wps-side-mid", { children: [slots.nav] });
|
|
627
637
|
var foot = el("div", "wps-foot");
|
|
628
|
-
var aiRow = navRow("sparkles", "Workpeg AI", bizHref("/chats/new")
|
|
638
|
+
var aiRow = navRow("sparkles", "Workpeg AI", bizHref("/chats/new"),
|
|
639
|
+
"wps-ai-row");
|
|
640
|
+
// On any /chats/* route this row IS the active surface (styled via
|
|
641
|
+
// .pg-chat-route in STYLES). A click must not reset an open conversation
|
|
642
|
+
// back to /chats/new — preventDefault so the runtime's link router skips
|
|
643
|
+
// navigating (it bails on defaultPrevented). Off the chat route the row
|
|
644
|
+
// navigates normally, opening a fresh chat.
|
|
645
|
+
aiRow.addEventListener("click", function (e) {
|
|
646
|
+
var de = document.documentElement;
|
|
647
|
+
if (de && de.classList && de.classList.contains("pg-chat-route")) {
|
|
648
|
+
e.preventDefault();
|
|
649
|
+
}
|
|
650
|
+
});
|
|
629
651
|
var rail = el("div", "pg-container pg-container-sidebar wps-sidebar", {
|
|
630
652
|
children: [
|
|
631
653
|
el("div", "wps-side-col", { children: [
|
|
@@ -778,6 +800,10 @@
|
|
|
778
800
|
"transition:padding-left 0.18s " + EASE + ";flex-shrink:0}",
|
|
779
801
|
".wps-nav-row:hover{background:var(--color-bg-active)}",
|
|
780
802
|
".wps-nav-row[aria-current=\"page\"]{background:var(--color-bg-active)}",
|
|
803
|
+
/* Workpeg AI stays active across the whole /chats/* surface, not just the
|
|
804
|
+
exact /chats/new href aria-current marks — driven by .pg-chat-route on
|
|
805
|
+
<html>, which the runtime flips on the reserved route. */
|
|
806
|
+
":root.pg-chat-route .wps-ai-row{background:var(--color-bg-active)}",
|
|
781
807
|
".wps-nav-row .ti{color:var(--color-icon-primary);flex-shrink:0}",
|
|
782
808
|
".wps-nav-label{flex:1;min-width:0;overflow:hidden;white-space:nowrap;",
|
|
783
809
|
"margin-left:8px;opacity:var(--pg-collapse-op);",
|
|
@@ -893,6 +919,9 @@
|
|
|
893
919
|
".wps-topnav-left{min-width:0;overflow:hidden;display:flex;",
|
|
894
920
|
"align-items:center}",
|
|
895
921
|
".wps-topnav-actions{display:flex;align-items:center;gap:8px}",
|
|
922
|
+
/* New chat only shows on an open conversation (same state as History) */
|
|
923
|
+
".wps-newchat{display:none}",
|
|
924
|
+
":root.pg-chat-route.wpai-chat-open .wps-newchat{display:flex}",
|
|
896
925
|
".wps-chrome-btn{display:flex;align-items:center;justify-content:center;",
|
|
897
926
|
"width:32px;height:32px;border-radius:var(--radius);cursor:pointer;",
|
|
898
927
|
"flex-shrink:0;border:none;background:transparent;padding:0;",
|
|
@@ -157,6 +157,43 @@
|
|
|
157
157
|
// target only Workpeg domains, resolve a path against apiBase, and send the
|
|
158
158
|
// Peg-Namespace whoami header. workpeg.get/post/... in Python compile here.
|
|
159
159
|
|
|
160
|
+
// The workpeg.* endpoint allowlist. Being on a Workpeg host is necessary
|
|
161
|
+
// but no longer sufficient: a namespaced call must ALSO resolve to one of
|
|
162
|
+
// these sanctioned surfaces, so a Peg can't reach arbitrary platform
|
|
163
|
+
// endpoints (/v1/users/me/, another business's data, …) through this
|
|
164
|
+
// client. Each rule may pin the host's first label and/or a pathname regex;
|
|
165
|
+
// a call is allowed if it satisfies any rule. Matched against the
|
|
166
|
+
// URL-NORMALIZED pathname, so ".." can't tunnel out of an allowed prefix.
|
|
167
|
+
// Extend this list as new first-class surfaces are opened to Pegs.
|
|
168
|
+
Workpeg._wpAllowlist = [
|
|
169
|
+
// Tables & rows data API — a table, its rows, bulk-create, search and
|
|
170
|
+
// permissions all live under /v1/businesses/<biz>/tables/. The business
|
|
171
|
+
// segment may be empty (off a business page); the server rejects that,
|
|
172
|
+
// and it stays a tables-shaped URL either way.
|
|
173
|
+
{ path: /^\/v1\/businesses\/[^/]*\/tables\// },
|
|
174
|
+
// The CDN — assets, pegment, bundles, peg media (any path).
|
|
175
|
+
{ host: "cdn" },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
Workpeg._wpAllowedEndpoint = function (full) {
|
|
179
|
+
var u;
|
|
180
|
+
try {
|
|
181
|
+
u = new URL(full);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
// First label of a Workpeg host (the caller has already vouched the host
|
|
186
|
+
// is ours), e.g. "cdn" of cdn.workpeg.com; pathname is normalized by URL.
|
|
187
|
+
var firstLabel = u.hostname.toLowerCase().split(".")[0];
|
|
188
|
+
for (var i = 0; i < Workpeg._wpAllowlist.length; i++) {
|
|
189
|
+
var rule = Workpeg._wpAllowlist[i];
|
|
190
|
+
if (rule.host && rule.host !== firstLabel) continue;
|
|
191
|
+
if (rule.path && !rule.path.test(u.pathname)) continue;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
};
|
|
196
|
+
|
|
160
197
|
Workpeg._wpBuildUrl = function (method, url) {
|
|
161
198
|
var full = /^(https?:)?\/\//i.test(String(url))
|
|
162
199
|
? String(url)
|
|
@@ -167,6 +204,13 @@
|
|
|
167
204
|
full + "); use http." + method.toLowerCase() + "() for the open web."
|
|
168
205
|
);
|
|
169
206
|
}
|
|
207
|
+
// …and only the allowlisted Workpeg surfaces (tables, rows, CDN).
|
|
208
|
+
if (!Workpeg._wpAllowedEndpoint(full)) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"workpeg." + method.toLowerCase() + "() may only reach the tables, " +
|
|
211
|
+
"rows and CDN endpoints, not " + full + "."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
170
214
|
return full;
|
|
171
215
|
};
|
|
172
216
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|