workpeg 0.77.0__tar.gz → 0.78.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.77.0/src/workpeg.egg-info → workpeg-0.78.0}/PKG-INFO +1 -1
- {workpeg-0.77.0 → workpeg-0.78.0}/pyproject.toml +1 -1
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/ai.js +51 -22
- {workpeg-0.77.0 → workpeg-0.78.0/src/workpeg.egg-info}/PKG-INFO +1 -1
- {workpeg-0.77.0 → workpeg-0.78.0}/LICENSE +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/MANIFEST.in +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/README.md +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/setup.cfg +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/build.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/cdn.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/cli.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/config.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/context.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/create_new.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/run.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/runtime.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/submit.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/Dockerfile +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/LICENSE +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/README.md +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/app/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/app/main.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/templates/functions/requirements.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/actions.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/app.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/build.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/ast_parser.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/ir.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/js_generator.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/create.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/fake_cloud.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/minify.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/pegment.css +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/workpeg-runtime.js +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/scaffold.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/serve.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/state.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/validators.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/watcher.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/__init__.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/base.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/canvas.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/display.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/forms.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/futures.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/gestures.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/interactive.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/layout.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg/utils.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg.egg-info/SOURCES.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg.egg-info/dependency_links.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg.egg-info/entry_points.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg.egg-info/requires.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/src/workpeg.egg-info/top_level.txt +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_build.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_cdn.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_changelog.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_changelog_guard.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_cli.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_config.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_context.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_create_new.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_release_script.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_run.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_runtime.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_submit.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_utils.py +0 -0
- {workpeg-0.77.0 → workpeg-0.78.0}/tests/test_watch_pipeline.py +0 -0
|
@@ -607,6 +607,7 @@
|
|
|
607
607
|
|
|
608
608
|
// ── conversation view (/chat/<uuid>) ───────────────────────────────────
|
|
609
609
|
var messagesEl = null;
|
|
610
|
+
var scrollDownEl = null; // the "scroll to latest" button (when scrolled up)
|
|
610
611
|
function renderConversation() {
|
|
611
612
|
S.host.innerHTML = "";
|
|
612
613
|
closePlusMenu();
|
|
@@ -614,7 +615,17 @@
|
|
|
614
615
|
var conversation = el("div", "chat-conversation", { children: [messagesEl] });
|
|
615
616
|
|
|
616
617
|
var composer = buildComposer("Message Workpeg AI…");
|
|
618
|
+
// A "scroll to latest" affordance — floats just above the (sticky) composer,
|
|
619
|
+
// shown only when the thread isn't scrolled to the bottom.
|
|
620
|
+
var scrollDown = el("button", "wpai-scroll-down", {
|
|
621
|
+
attrs: { type: "button", "aria-label": "Scroll to latest",
|
|
622
|
+
title: "Scroll to latest" },
|
|
623
|
+
children: [icon("arrow-down", 20)]
|
|
624
|
+
});
|
|
625
|
+
scrollDown.addEventListener("click", function () { scrollToBottom(true); });
|
|
626
|
+
scrollDownEl = scrollDown;
|
|
617
627
|
var bar = el("div", "chat-input-bar", { children: [
|
|
628
|
+
scrollDown,
|
|
618
629
|
el("div", "chat-input-wrap-holder", { children: [composer] })
|
|
619
630
|
] });
|
|
620
631
|
|
|
@@ -623,7 +634,13 @@
|
|
|
623
634
|
S.host.appendChild(center);
|
|
624
635
|
|
|
625
636
|
S.messages.forEach(function (m) { messagesEl.appendChild(renderMessage(m)); });
|
|
626
|
-
|
|
637
|
+
// The surface (#workpeg-ai-root) scrolls — toggle the scroll-to-latest
|
|
638
|
+
// button from there. Rebind (remove+add) so re-renders/re-attaches keep
|
|
639
|
+
// exactly one listener on the current host.
|
|
640
|
+
if (S.host) {
|
|
641
|
+
S.host.removeEventListener("scroll", updateScrollDown);
|
|
642
|
+
S.host.addEventListener("scroll", updateScrollDown);
|
|
643
|
+
}
|
|
627
644
|
// Delegated copy for code blocks (their markup is set via innerHTML, so we
|
|
628
645
|
// can't bind per-button). Copies the block's source text.
|
|
629
646
|
messagesEl.addEventListener("click", function (e) {
|
|
@@ -637,7 +654,7 @@
|
|
|
637
654
|
setTimeout(function () { btn.textContent = "Copy"; }, 1200);
|
|
638
655
|
});
|
|
639
656
|
scrollToBottom();
|
|
640
|
-
|
|
657
|
+
updateScrollDown();
|
|
641
658
|
}
|
|
642
659
|
|
|
643
660
|
function renderMessage(m) {
|
|
@@ -779,7 +796,7 @@
|
|
|
779
796
|
if (messagesEl) { messagesEl.appendChild(renderMessage(userMsg)); scrollToBottom(); }
|
|
780
797
|
|
|
781
798
|
var thinking = thinkingNode();
|
|
782
|
-
if (messagesEl) { messagesEl.appendChild(thinking); scrollToBottom();
|
|
799
|
+
if (messagesEl) { messagesEl.appendChild(thinking); scrollToBottom(); updateScrollDown(); }
|
|
783
800
|
|
|
784
801
|
return json("POST", chatsPath(S.chatId + "/messages/"), { content: text })
|
|
785
802
|
.then(function (created) {
|
|
@@ -983,16 +1000,21 @@
|
|
|
983
1000
|
}
|
|
984
1001
|
|
|
985
1002
|
// ── scroll / fade ──────────────────────────────────────────────────────
|
|
986
|
-
function scrollToBottom() {
|
|
987
|
-
|
|
1003
|
+
function scrollToBottom(smooth) {
|
|
1004
|
+
// The whole surface scrolls (see mount): pin it to the newest message.
|
|
1005
|
+
if (!S.host) return;
|
|
1006
|
+
if (smooth && S.host.scrollTo) {
|
|
1007
|
+
S.host.scrollTo({ top: S.host.scrollHeight, behavior: "smooth" });
|
|
1008
|
+
} else {
|
|
1009
|
+
S.host.scrollTop = S.host.scrollHeight;
|
|
1010
|
+
}
|
|
988
1011
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
var
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
messagesEl.classList.toggle("is-fade-bottom", bottom);
|
|
1012
|
+
// Show the scroll-to-latest button only when the thread isn't at the bottom.
|
|
1013
|
+
function updateScrollDown() {
|
|
1014
|
+
if (!scrollDownEl || !S.host) return;
|
|
1015
|
+
var atBottom = S.host.scrollTop + S.host.clientHeight >=
|
|
1016
|
+
S.host.scrollHeight - 32;
|
|
1017
|
+
scrollDownEl.classList.toggle("is-visible", !atBottom);
|
|
996
1018
|
}
|
|
997
1019
|
|
|
998
1020
|
// ── misc helpers ───────────────────────────────────────────────────────
|
|
@@ -1077,9 +1099,12 @@
|
|
|
1077
1099
|
host.parentNode.style.position = "relative";
|
|
1078
1100
|
}
|
|
1079
1101
|
if (host.setAttribute) {
|
|
1102
|
+
// We are the SCROLL container: a conversation scrolls on #workpeg-ai-root
|
|
1103
|
+
// itself (so the scrollbar rides the surface's far-right edge, not an inner
|
|
1104
|
+
// column), and the composer sticks to the bottom inside us.
|
|
1080
1105
|
host.setAttribute("style",
|
|
1081
1106
|
"position:absolute;top:0;right:0;bottom:0;left:0;display:block;" +
|
|
1082
|
-
"margin:0;padding:0;overflow:hidden;box-sizing:border-box");
|
|
1107
|
+
"margin:0;padding:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box");
|
|
1083
1108
|
}
|
|
1084
1109
|
if (reattach) { redraw(chatId); return; }
|
|
1085
1110
|
S.rendered = null; // force a fresh render on (re)mount
|
|
@@ -1124,7 +1149,8 @@
|
|
|
1124
1149
|
// The landing centres in the area BELOW the top nav, so it reads a touch low;
|
|
1125
1150
|
// a bottom pad (~the nav height) lifts it to look centred in the whole panel.
|
|
1126
1151
|
R + " .chat-center{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:32px;min-height:0;height:100%;overflow:hidden;position:relative;padding-bottom:var(--wpai-nav-h,64px)}",
|
|
1127
|
-
|
|
1152
|
+
// conversation centre: grow with the thread (don't clip) so the host scrolls
|
|
1153
|
+
R + " .wpai-center--convo{justify-content:flex-start;padding-bottom:0;gap:0;height:auto;min-height:100%;overflow:visible}",
|
|
1128
1154
|
R + " .chat-landing{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:32px;width:100%;padding:40px 24px}",
|
|
1129
1155
|
R + " .chat-greeting{font-size:var(--text-headline-lg-size,2rem);line-height:40px;font-weight:var(--font-weight-medium,500);color:var(--color-text-primary);text-align:center;margin:0}",
|
|
1130
1156
|
// the landing card: one subtle border around the composer + suggestions
|
|
@@ -1179,13 +1205,9 @@
|
|
|
1179
1205
|
R + " .chat-plus-item--chevron{justify-content:space-between}",
|
|
1180
1206
|
R + " .chat-plus-item--chevron span{display:flex;align-items:center;gap:10px}",
|
|
1181
1207
|
R + " .chat-plus-divider{height:1px;background:var(--color-border-subtle);margin:4px}",
|
|
1182
|
-
// conversation
|
|
1183
|
-
R + " .chat-conversation{flex:1
|
|
1184
|
-
R + " .chat-messages{
|
|
1185
|
-
R + " .chat-messages::-webkit-scrollbar{display:none}",
|
|
1186
|
-
R + " .chat-messages.is-fade-top{-webkit-mask-image:linear-gradient(to bottom,transparent 0,black var(--fade-size));mask-image:linear-gradient(to bottom,transparent 0,black var(--fade-size))}",
|
|
1187
|
-
R + " .chat-messages.is-fade-bottom{-webkit-mask-image:linear-gradient(to bottom,black calc(100% - var(--fade-size)),transparent 100%);mask-image:linear-gradient(to bottom,black calc(100% - var(--fade-size)),transparent 100%)}",
|
|
1188
|
-
R + " .chat-messages.is-fade-top.is-fade-bottom{-webkit-mask-image:linear-gradient(to bottom,transparent 0,black var(--fade-size),black calc(100% - var(--fade-size)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0,black var(--fade-size),black calc(100% - var(--fade-size)),transparent 100%)}",
|
|
1208
|
+
// conversation — the THREAD flows and grows; #workpeg-ai-root scrolls it.
|
|
1209
|
+
R + " .chat-conversation{flex:1 0 auto;width:100%;max-width:800px;margin:0 auto;display:flex;flex-direction:column;padding:24px 24px 32px}",
|
|
1210
|
+
R + " .chat-messages{display:flex;flex-direction:column;gap:32px;width:min(720px,100%);margin:0 auto}",
|
|
1189
1211
|
R + " .chat-message{display:flex;gap:12px;animation:wpaiMessageIn .3s ease}",
|
|
1190
1212
|
"@keyframes wpaiMessageIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}",
|
|
1191
1213
|
R + " .chat-message--user{justify-content:flex-end}",
|
|
@@ -1236,7 +1258,14 @@
|
|
|
1236
1258
|
R + " .chat-message--thinking .chat-message__thinking{display:flex;align-items:center;gap:8px}",
|
|
1237
1259
|
R + " .thinking-text{font-size:var(--text-body-sm-size,13px);color:var(--color-text-secondary)}",
|
|
1238
1260
|
// bottom input bar
|
|
1239
|
-
|
|
1261
|
+
// the composer sticks to the bottom of the scrolling surface; its gradient
|
|
1262
|
+
// lets the thread fade into it as messages scroll behind.
|
|
1263
|
+
R + " .chat-input-bar{position:sticky;bottom:0;align-self:center;width:min(720px,calc(100% - 48px));z-index:50;padding:12px 0 24px;background:linear-gradient(to bottom,transparent,var(--color-bg-default) 20px)}",
|
|
1264
|
+
// scroll-to-latest button — floats above the sticky composer, hidden until
|
|
1265
|
+
// the thread is scrolled up.
|
|
1266
|
+
R + " .wpai-scroll-down{position:absolute;left:50%;top:-46px;transform:translateX(-50%) translateY(6px);width:36px;height:36px;border-radius:9999px;display:flex;align-items:center;justify-content:center;background:var(--color-surface-raised,var(--color-surface-default));border:1px solid var(--color-border-subtle);color:var(--color-icon-primary,var(--color-text-primary));cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.28);opacity:0;visibility:hidden;pointer-events:none;transition:opacity .15s ease,transform .15s ease,visibility .15s}",
|
|
1267
|
+
R + " .wpai-scroll-down.is-visible{opacity:1;visibility:visible;pointer-events:auto;transform:translateX(-50%) translateY(0)}",
|
|
1268
|
+
R + " .wpai-scroll-down:hover{background:var(--color-surface-sunken2,var(--color-surface-default))}",
|
|
1240
1269
|
// banner
|
|
1241
1270
|
// The announcement floats OVER the top nav (fixed to the viewport, so the
|
|
1242
1271
|
// host's overflow can't clip it), centred in the content area to the right of
|
|
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
|