workpeg 0.97.0__tar.gz → 0.97.2__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.97.0/src/workpeg.egg-info → workpeg-0.97.2}/PKG-INFO +1 -1
- {workpeg-0.97.0 → workpeg-0.97.2}/pyproject.toml +1 -1
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/app.py +23 -3
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/ast_parser.py +12 -2
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/create.py +3 -3
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/ai.js +1 -1
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/scaffold.js +22 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/workpeg-runtime.js +16 -0
- {workpeg-0.97.0 → workpeg-0.97.2/src/workpeg.egg-info}/PKG-INFO +1 -1
- {workpeg-0.97.0 → workpeg-0.97.2}/LICENSE +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/MANIFEST.in +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/README.md +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/setup.cfg +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/build.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/cdn.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/cli.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/config.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/context.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/create_new.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/run.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/runtime.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/submit.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/Dockerfile +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/LICENSE +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/README.md +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/app/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/app/main.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/requirements.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/actions.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/build.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/ir.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/js_generator.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/fake_cloud.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/minify.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/pegment.css +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/scaffold.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/serve.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/state.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/validators.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/watcher.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/__init__.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/base.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/canvas.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/display.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/forms.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/futures.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/gestures.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/interactive.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/layout.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/utils.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/SOURCES.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/dependency_links.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/entry_points.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/requires.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/top_level.txt +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_build.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cdn.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cdn_publish.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_changelog.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_changelog_guard.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cli.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_config.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_context.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_create_new.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_release_script.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_run.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_runtime.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_submit.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_utils.py +0 -0
- {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_watch_pipeline.py +0 -0
|
@@ -712,6 +712,26 @@ class _Workpeg:
|
|
|
712
712
|
"""
|
|
713
713
|
return self._unavailable("get_tables")
|
|
714
714
|
|
|
715
|
+
def first_business(self) -> Any:
|
|
716
|
+
"""Resolve the signed-in user's primary business — its ``uuid`` string,
|
|
717
|
+
or ``""`` when the user has none.
|
|
718
|
+
|
|
719
|
+
Meant for a bare ``/`` landing that redirects into a business::
|
|
720
|
+
|
|
721
|
+
class Landing(App):
|
|
722
|
+
async def on_mount(self):
|
|
723
|
+
biz = await workpeg.first_business()
|
|
724
|
+
if biz:
|
|
725
|
+
navigate("/b/" + biz)
|
|
726
|
+
|
|
727
|
+
The businesses list is a *session* endpoint the ``workpeg.*`` allowlist
|
|
728
|
+
excludes (only tables, rows and the CDN are reachable), so — unlike
|
|
729
|
+
``workpeg.get`` — the runtime resolves this one credentialed and
|
|
730
|
+
allowlist-exempt, the same chrome path :class:`Authenticated` uses for
|
|
731
|
+
``/v1/users/me/``. Compiles to ``wpFirstBusiness``; never runs in Python.
|
|
732
|
+
"""
|
|
733
|
+
return self._unavailable("first_business")
|
|
734
|
+
|
|
715
735
|
# ``*_protected`` variants additionally send the session cookie
|
|
716
736
|
# (``credentials: "include"``) — the authenticated request, for endpoints
|
|
717
737
|
# behind a login (the recreation of the React ``fetchProtected`` helper)::
|
|
@@ -755,9 +775,9 @@ class _Cookie:
|
|
|
755
775
|
if biz:
|
|
756
776
|
navigate(f"/{biz}")
|
|
757
777
|
else:
|
|
758
|
-
|
|
759
|
-
if
|
|
760
|
-
navigate(f"/{
|
|
778
|
+
first = await workpeg.first_business()
|
|
779
|
+
if first:
|
|
780
|
+
navigate(f"/{first}")
|
|
761
781
|
|
|
762
782
|
They compile to the runtime's cookie helpers (name/value percent-encoded, so
|
|
763
783
|
neither can inject extra cookie attributes); never executed in Python.
|
|
@@ -74,6 +74,15 @@ _WORKPEG_TABLE_HELPERS = {
|
|
|
74
74
|
"delete_row": (2, 2, "workpeg.delete_row(table_uuid, row_id)"),
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
# Curated session/chrome helpers on workpeg.* — no URL, no arbitrary reach.
|
|
78
|
+
# ``first_business`` resolves the signed-in user's primary business uuid: the
|
|
79
|
+
# businesses list is a session endpoint the peg allowlist excludes, so the
|
|
80
|
+
# runtime fetches it credentialed (like Authenticated's /v1/users/me/) rather
|
|
81
|
+
# than through workpeg.get. For the bare "/" landing → redirect to /b/<uuid>.
|
|
82
|
+
_WORKPEG_SESSION_HELPERS = {
|
|
83
|
+
"first_business": (0, 0, "workpeg.first_business()"),
|
|
84
|
+
}
|
|
85
|
+
|
|
77
86
|
|
|
78
87
|
def _is_workpeg_host(url: str) -> bool:
|
|
79
88
|
"""True when *url* targets a Workpeg-owned host — ``workpeg`` as the
|
|
@@ -4107,8 +4116,9 @@ class ASTParser:
|
|
|
4107
4116
|
# Tables & Rows helpers (get_tables, add_row, …) take no URL — the
|
|
4108
4117
|
# business id is read from the page URL at runtime, and the runtime
|
|
4109
4118
|
# builds the endpoint path from the args.
|
|
4110
|
-
if method in _WORKPEG_TABLE_HELPERS:
|
|
4111
|
-
lo, hi, usage = _WORKPEG_TABLE_HELPERS
|
|
4119
|
+
if method in _WORKPEG_TABLE_HELPERS or method in _WORKPEG_SESSION_HELPERS:
|
|
4120
|
+
lo, hi, usage = (_WORKPEG_TABLE_HELPERS.get(method)
|
|
4121
|
+
or _WORKPEG_SESSION_HELPERS[method])
|
|
4112
4122
|
# get_rows windows large tables server-side; the two keywords are
|
|
4113
4123
|
# the standard way to fetch from an unbounded table. The response
|
|
4114
4124
|
# stays a bare array either way (no pagination envelope).
|
|
@@ -52,9 +52,9 @@ class Landing(App):
|
|
|
52
52
|
if cached:
|
|
53
53
|
navigate("/b/" + cached)
|
|
54
54
|
else:
|
|
55
|
-
|
|
56
|
-
if
|
|
57
|
-
navigate("/b/" +
|
|
55
|
+
biz = await workpeg.first_business()
|
|
56
|
+
if biz:
|
|
57
|
+
navigate("/b/" + biz)
|
|
58
58
|
|
|
59
59
|
def build(self):
|
|
60
60
|
return Authenticated(
|
|
@@ -2922,7 +2922,7 @@
|
|
|
2922
2922
|
R + " .wpai-tok-number{color:#b5730a}",
|
|
2923
2923
|
R + " .wpai-tok-fn{color:#3f7bd6}",
|
|
2924
2924
|
R + " .wpai-tok-const{color:#c0663a}",
|
|
2925
|
-
R + " .chat-message__bubble a{color:var(--color-
|
|
2925
|
+
R + " .chat-message__bubble a{color:var(--color-text-link);text-decoration:underline}",
|
|
2926
2926
|
R + " .chat-message__actions{display:flex;align-items:center;gap:4px;margin-top:4px;opacity:0;transition:opacity .15s ease}",
|
|
2927
2927
|
R + " .chat-message--ai:hover .chat-message__actions{opacity:1}",
|
|
2928
2928
|
R + " .chat-message--ai.wpai-streaming .chat-message__actions{opacity:0}",
|
|
@@ -409,12 +409,26 @@
|
|
|
409
409
|
return row;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
// A full-screen "Signing out…" cover, shown the instant Log out is clicked so
|
|
413
|
+
// the DELETE round-trip + redirect never reads as an unresponsive pause. It
|
|
414
|
+
// stays until the redirect navigates away, so there's nothing to remove.
|
|
415
|
+
function showLogoutOverlay() {
|
|
416
|
+
if (typeof document === "undefined" || !document.body) return;
|
|
417
|
+
if (document.getElementById("wps-logout-overlay")) return; // idempotent
|
|
418
|
+
var ov = el("div", "wps-logout-overlay", { attrs: {
|
|
419
|
+
id: "wps-logout-overlay", role: "status", "aria-label": "Signing out" } });
|
|
420
|
+
ov.appendChild(el("div", "wps-spin"));
|
|
421
|
+
ov.appendChild(el("div", "wps-logout-label", { text: "Signing out…" }));
|
|
422
|
+
document.body.appendChild(ov);
|
|
423
|
+
}
|
|
424
|
+
|
|
412
425
|
// Log out — mirrors space's handlelogOut: DELETE the token (the server drops
|
|
413
426
|
// the HttpOnly access/refresh cookies), wipe the readable auth cookies, then
|
|
414
427
|
// bounce to the accounts login (the same redirect the auth gate uses). A
|
|
415
428
|
// direct credentialed fetch — the workpeg.* client's allowlist (tables /
|
|
416
429
|
// rows / CDN) intentionally won't reach /users/token/.
|
|
417
430
|
function logout() {
|
|
431
|
+
showLogoutOverlay(); // immediate feedback before the async pause
|
|
418
432
|
var api = String(WP().apiBase || "https://api.workpeg.com")
|
|
419
433
|
.replace(/\/+$/, "");
|
|
420
434
|
var finish = function () {
|
|
@@ -904,6 +918,14 @@
|
|
|
904
918
|
"border-top-color:var(--color-text-tertiary);",
|
|
905
919
|
"animation:wps-spin 0.8s linear infinite}",
|
|
906
920
|
"@keyframes wps-spin{to{transform:rotate(360deg)}}",
|
|
921
|
+
/* the "Signing out…" cover — an instant full-screen loader on Log out */
|
|
922
|
+
".wps-logout-overlay{position:fixed;inset:0;z-index:2147483000;",
|
|
923
|
+
"display:flex;flex-direction:column;align-items:center;",
|
|
924
|
+
"justify-content:center;gap:14px;background:var(--color-bg-default);",
|
|
925
|
+
"animation:wps-logout-fade 0.12s ease}",
|
|
926
|
+
".wps-logout-overlay .wps-spin{width:28px;height:28px;border-width:3px}",
|
|
927
|
+
".wps-logout-label{color:var(--color-text-secondary);font-size:14px}",
|
|
928
|
+
"@keyframes wps-logout-fade{from{opacity:0}to{opacity:1}}",
|
|
907
929
|
|
|
908
930
|
/* the user / business switcher */
|
|
909
931
|
".wps-user{display:flex;align-items:center;width:100%;",
|
|
@@ -270,6 +270,22 @@
|
|
|
270
270
|
return Workpeg.wpRequest("DELETE", url, data, true);
|
|
271
271
|
};
|
|
272
272
|
|
|
273
|
+
// The signed-in user's businesses is a session/chrome endpoint — the peg
|
|
274
|
+
// client's allowlist deliberately excludes it (as it does /v1/users/me/), so
|
|
275
|
+
// it's resolved HERE, credentialed and allowlist-exempt (a raw Workpeg._request,
|
|
276
|
+
// not wpRequest), exactly like Authenticated's /v1/users/me/ check. It's the
|
|
277
|
+
// one businesses call the chrome doesn't already own — the bare "/" landing,
|
|
278
|
+
// which runs before scaffold.js mounts. Returns the first business's uuid ("" if
|
|
279
|
+
// the user has none), for a redirect to /b/<uuid>.
|
|
280
|
+
Workpeg.wpFirstBusiness = function () {
|
|
281
|
+
var url = Workpeg.apiBase.replace(/\/+$/, "") + "/v1/businesses/";
|
|
282
|
+
return Workpeg._ensureFreshSession().then(function () {
|
|
283
|
+
return Workpeg._request("GET", url, undefined, {}, true);
|
|
284
|
+
}).then(function (list) {
|
|
285
|
+
return (list && list[0] && list[0].uuid) || "";
|
|
286
|
+
});
|
|
287
|
+
};
|
|
288
|
+
|
|
273
289
|
// The active business id, read from the URL. Pages live under "/b/<id>/...",
|
|
274
290
|
// so the id is the path segment right after a leading "b" ("" if the page
|
|
275
291
|
// isn't business-scoped).
|
|
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
|