workpeg 0.76.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.
Files changed (72) hide show
  1. {workpeg-0.76.0/src/workpeg.egg-info → workpeg-0.78.0}/PKG-INFO +1 -1
  2. {workpeg-0.76.0 → workpeg-0.78.0}/pyproject.toml +1 -1
  3. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/ai.js +52 -29
  4. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/scaffold.py +10 -2
  5. {workpeg-0.76.0 → workpeg-0.78.0/src/workpeg.egg-info}/PKG-INFO +1 -1
  6. {workpeg-0.76.0 → workpeg-0.78.0}/LICENSE +0 -0
  7. {workpeg-0.76.0 → workpeg-0.78.0}/MANIFEST.in +0 -0
  8. {workpeg-0.76.0 → workpeg-0.78.0}/README.md +0 -0
  9. {workpeg-0.76.0 → workpeg-0.78.0}/setup.cfg +0 -0
  10. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/__init__.py +0 -0
  11. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/build.py +0 -0
  12. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/cdn.py +0 -0
  13. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/cli.py +0 -0
  14. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/config.py +0 -0
  15. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/context.py +0 -0
  16. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/create_new.py +0 -0
  17. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/run.py +0 -0
  18. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/runtime.py +0 -0
  19. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/submit.py +0 -0
  20. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/__init__.py +0 -0
  21. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/Dockerfile +0 -0
  22. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/LICENSE +0 -0
  23. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/README.md +0 -0
  24. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/app/__init__.py +0 -0
  25. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/app/main.py +0 -0
  26. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/templates/functions/requirements.txt +0 -0
  27. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/__init__.py +0 -0
  28. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/actions.py +0 -0
  29. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/app.py +0 -0
  30. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/build.py +0 -0
  31. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/__init__.py +0 -0
  32. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/ast_parser.py +0 -0
  33. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/ir.py +0 -0
  34. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/compiler/js_generator.py +0 -0
  35. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/create.py +0 -0
  36. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/fake_cloud.py +0 -0
  37. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/minify.py +0 -0
  38. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/pegment.css +0 -0
  39. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/runtime/workpeg-runtime.js +0 -0
  40. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/serve.py +0 -0
  41. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/state.py +0 -0
  42. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/validators.py +0 -0
  43. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/watcher.py +0 -0
  44. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/__init__.py +0 -0
  45. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/base.py +0 -0
  46. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/canvas.py +0 -0
  47. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/display.py +0 -0
  48. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/forms.py +0 -0
  49. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/futures.py +0 -0
  50. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/gestures.py +0 -0
  51. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/interactive.py +0 -0
  52. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/ui/widgets/layout.py +0 -0
  53. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg/utils.py +0 -0
  54. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg.egg-info/SOURCES.txt +0 -0
  55. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg.egg-info/dependency_links.txt +0 -0
  56. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg.egg-info/entry_points.txt +0 -0
  57. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg.egg-info/requires.txt +0 -0
  58. {workpeg-0.76.0 → workpeg-0.78.0}/src/workpeg.egg-info/top_level.txt +0 -0
  59. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_build.py +0 -0
  60. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_cdn.py +0 -0
  61. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_changelog.py +0 -0
  62. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_changelog_guard.py +0 -0
  63. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_cli.py +0 -0
  64. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_config.py +0 -0
  65. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_context.py +0 -0
  66. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_create_new.py +0 -0
  67. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_release_script.py +0 -0
  68. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_run.py +0 -0
  69. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_runtime.py +0 -0
  70. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_submit.py +0 -0
  71. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_utils.py +0 -0
  72. {workpeg-0.76.0 → workpeg-0.78.0}/tests/test_watch_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workpeg
3
- Version: 0.76.0
3
+ Version: 0.78.0
4
4
  Summary: Workpeg function runtime and SDK
5
5
  Author-email: Workpeg <support@workpeg.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "workpeg"
7
- version = "0.76.0"
7
+ version = "0.78.0"
8
8
  description = "Workpeg function runtime and SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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
- messagesEl.addEventListener("scroll", updateFade);
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
- updateFade();
657
+ updateScrollDown();
641
658
  }
642
659
 
643
660
  function renderMessage(m) {
@@ -737,9 +754,7 @@
737
754
  el("div", "chat-message__response-header", { children: [
738
755
  thinkingVideoNode(),
739
756
  el("div", "chat-message__thinking", { children: [
740
- el("span", "thinking-text", { text: "Thinking" }),
741
- el("span", "thinking-dot"), el("span", "thinking-dot"),
742
- el("span", "thinking-dot")
757
+ el("span", "thinking-text", { text: "Thinking" })
743
758
  ] })
744
759
  ] })
745
760
  ]
@@ -781,7 +796,7 @@
781
796
  if (messagesEl) { messagesEl.appendChild(renderMessage(userMsg)); scrollToBottom(); }
782
797
 
783
798
  var thinking = thinkingNode();
784
- if (messagesEl) { messagesEl.appendChild(thinking); scrollToBottom(); updateFade(); }
799
+ if (messagesEl) { messagesEl.appendChild(thinking); scrollToBottom(); updateScrollDown(); }
785
800
 
786
801
  return json("POST", chatsPath(S.chatId + "/messages/"), { content: text })
787
802
  .then(function (created) {
@@ -985,16 +1000,21 @@
985
1000
  }
986
1001
 
987
1002
  // ── scroll / fade ──────────────────────────────────────────────────────
988
- function scrollToBottom() {
989
- if (messagesEl) messagesEl.scrollTop = messagesEl.scrollHeight;
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
+ }
990
1011
  }
991
- function updateFade() {
992
- if (!messagesEl) return;
993
- var top = messagesEl.scrollTop > 4;
994
- var bottom = messagesEl.scrollTop + messagesEl.clientHeight <
995
- messagesEl.scrollHeight - 4;
996
- messagesEl.classList.toggle("is-fade-top", top);
997
- 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);
998
1018
  }
999
1019
 
1000
1020
  // ── misc helpers ───────────────────────────────────────────────────────
@@ -1079,9 +1099,12 @@
1079
1099
  host.parentNode.style.position = "relative";
1080
1100
  }
1081
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.
1082
1105
  host.setAttribute("style",
1083
1106
  "position:absolute;top:0;right:0;bottom:0;left:0;display:block;" +
1084
- "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");
1085
1108
  }
1086
1109
  if (reattach) { redraw(chatId); return; }
1087
1110
  S.rendered = null; // force a fresh render on (re)mount
@@ -1126,7 +1149,8 @@
1126
1149
  // The landing centres in the area BELOW the top nav, so it reads a touch low;
1127
1150
  // a bottom pad (~the nav height) lifts it to look centred in the whole panel.
1128
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)}",
1129
- R + " .wpai-center--convo{justify-content:flex-start;padding-bottom:0}",
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}",
1130
1154
  R + " .chat-landing{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:32px;width:100%;padding:40px 24px}",
1131
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}",
1132
1156
  // the landing card: one subtle border around the composer + suggestions
@@ -1181,13 +1205,9 @@
1181
1205
  R + " .chat-plus-item--chevron{justify-content:space-between}",
1182
1206
  R + " .chat-plus-item--chevron span{display:flex;align-items:center;gap:10px}",
1183
1207
  R + " .chat-plus-divider{height:1px;background:var(--color-border-subtle);margin:4px}",
1184
- // conversation
1185
- R + " .chat-conversation{flex:1;min-height:0;width:100%;max-width:800px;margin:0 auto;display:flex;flex-direction:column;padding:0 24px 100px;overflow:hidden}",
1186
- R + " .chat-messages{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:32px;margin:0 auto 50px auto;width:min(720px,100%);--fade-size:48px}",
1187
- R + " .chat-messages::-webkit-scrollbar{display:none}",
1188
- 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))}",
1189
- 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%)}",
1190
- 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}",
1191
1211
  R + " .chat-message{display:flex;gap:12px;animation:wpaiMessageIn .3s ease}",
1192
1212
  "@keyframes wpaiMessageIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}",
1193
1213
  R + " .chat-message--user{justify-content:flex-end}",
@@ -1237,12 +1257,15 @@
1237
1257
  // thinking
1238
1258
  R + " .chat-message--thinking .chat-message__thinking{display:flex;align-items:center;gap:8px}",
1239
1259
  R + " .thinking-text{font-size:var(--text-body-sm-size,13px);color:var(--color-text-secondary)}",
1240
- R + " .thinking-dot{width:6px;height:6px;background:var(--color-brand-secondary,var(--color-text-secondary));border-radius:50%;animation:wpaiThinking 1.4s ease-in-out infinite}",
1241
- R + " .thinking-dot:nth-child(2){animation-delay:.2s}",
1242
- R + " .thinking-dot:nth-child(3){animation-delay:.4s}",
1243
- "@keyframes wpaiThinking{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}",
1244
1260
  // bottom input bar
1245
- R + " .chat-input-bar{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);width:min(720px,calc(100% - 48px));z-index:50}",
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))}",
1246
1269
  // banner
1247
1270
  // The announcement floats OVER the top nav (fixed to the viewport, so the
1248
1271
  // host's overflow can't clip it), centred in the content area to the right of
@@ -1624,13 +1624,21 @@ class Scaffold(Component):
1624
1624
  "flexShrink": "0"},
1625
1625
  ),
1626
1626
  self.main,
1627
- gap=0, align="stretch", scroll="auto",
1627
+ # The SCROLL lives on the .pg-main container (below), like the
1628
+ # static design — so the scrollbar rides the whole main area's
1629
+ # right edge, not an inner column inset by the padding. This
1630
+ # column stays `scroll="visible"` only to keep its height:100%
1631
+ # (so the Outlet's flex chain — and the AI page's absolute
1632
+ # fill — still bounds), without a scrollbar of its own.
1633
+ gap=0, align="stretch", scroll="visible",
1628
1634
  ),
1629
1635
  style={
1630
1636
  "flex": "1", "minWidth": "0",
1631
1637
  "background": "var(--color-bg-default)",
1632
1638
  "boxSizing": "border-box",
1633
- "overflow": "hidden", "padding": "0 16px",
1639
+ # scroll on the container itself (the design's .pg-main);
1640
+ # padding 0 24px 8px 24px (was 0 16px).
1641
+ "overflow": "auto", "padding": "0 24px 8px 24px",
1634
1642
  "display": "flex", "flexDirection": "column",
1635
1643
  },
1636
1644
  ),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workpeg
3
- Version: 0.76.0
3
+ Version: 0.78.0
4
4
  Summary: Workpeg function runtime and SDK
5
5
  Author-email: Workpeg <support@workpeg.com>
6
6
  License: MIT
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