@octaviaflow/core 3.1.0-beta.111 → 3.1.0-beta.112

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/dist/index.js CHANGED
@@ -11734,7 +11734,10 @@ function shortReferenceLabel(token) {
11734
11734
  if (token.startsWith("{{ods://")) {
11735
11735
  const path = token.slice(8, -2);
11736
11736
  const parts = path.split("/");
11737
- if (parts[0] === "steps" && parts.length >= 4) return parts.slice(3).join(".");
11737
+ if (parts[0] === "steps") {
11738
+ if (parts.length >= 4) return parts.slice(3).join(".");
11739
+ return parts[2] ?? "output";
11740
+ }
11738
11741
  if (parts[0] === "flow") return `flow.${parts.slice(1).join(".")}`;
11739
11742
  return parts.join(".");
11740
11743
  }
@@ -33209,6 +33212,7 @@ function SlideoutContent({
33209
33212
  title,
33210
33213
  children,
33211
33214
  footer,
33215
+ headerActions,
33212
33216
  className,
33213
33217
  offsetTop = 0,
33214
33218
  offsetBottom = 0
@@ -33294,16 +33298,19 @@ function SlideoutContent({
33294
33298
  children: [
33295
33299
  title && /* @__PURE__ */ jsxs130("div", { className: "ods-slideout__header", children: [
33296
33300
  /* @__PURE__ */ jsx135("h2", { ...titleProps, className: "ods-slideout__title", children: title }),
33297
- /* @__PURE__ */ jsx135(
33298
- "button",
33299
- {
33300
- className: "ods-slideout__close",
33301
- onClick: onClose,
33302
- "aria-label": "Close panel",
33303
- type: "button",
33304
- children: /* @__PURE__ */ jsx135("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx135("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) })
33305
- }
33306
- )
33301
+ /* @__PURE__ */ jsxs130("div", { className: "ods-slideout__header-actions", children: [
33302
+ headerActions,
33303
+ /* @__PURE__ */ jsx135(
33304
+ "button",
33305
+ {
33306
+ className: "ods-slideout__close",
33307
+ onClick: onClose,
33308
+ "aria-label": "Close panel",
33309
+ type: "button",
33310
+ children: /* @__PURE__ */ jsx135("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx135("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) })
33311
+ }
33312
+ )
33313
+ ] })
33307
33314
  ] }),
33308
33315
  /* @__PURE__ */ jsx135("div", { className: "ods-slideout__body", children }),
33309
33316
  footer && /* @__PURE__ */ jsx135("div", { className: "ods-slideout__footer", children: footer })
@@ -34661,6 +34668,8 @@ import {
34661
34668
  createContext as createContext3,
34662
34669
  useCallback as useCallback51,
34663
34670
  useContext as useContext4,
34671
+ useEffect as useEffect57,
34672
+ useLayoutEffect as useLayoutEffect12,
34664
34673
  useMemo as useMemo37,
34665
34674
  useRef as useRef50,
34666
34675
  useState as useState71
@@ -34755,15 +34764,57 @@ var defaultIcons = {
34755
34764
  ] })
34756
34765
  };
34757
34766
  var ToastContext = createContext3(null);
34758
- function ToastBody({ item, onDismiss }) {
34767
+ function ToastBody({ item, stacked, onDismiss }) {
34759
34768
  const dismiss = () => onDismiss(item.id);
34769
+ const [expanded, setExpanded] = useState71(false);
34770
+ const [overflowing, setOverflowing] = useState71(false);
34771
+ const descRef = useRef50(null);
34772
+ const cut = item.clampLines > 0 && !expanded;
34773
+ useLayoutEffect12(() => {
34774
+ if (!cut) return;
34775
+ const el = descRef.current;
34776
+ if (el) setOverflowing(el.scrollHeight > el.clientHeight + 1);
34777
+ }, [cut, item.description]);
34760
34778
  if (item.render) return /* @__PURE__ */ jsx147(Fragment61, { children: item.render({ id: item.id, dismiss }) });
34761
34779
  const title = item.title ?? item.message;
34780
+ const toggles = overflowing && !stacked;
34781
+ const toggle = () => setExpanded((v) => !v);
34782
+ const onTextClick = (e) => {
34783
+ if (!toggles) return;
34784
+ if (window.getSelection()?.toString()) return;
34785
+ e.stopPropagation();
34786
+ toggle();
34787
+ };
34762
34788
  return /* @__PURE__ */ jsxs141(Fragment61, { children: [
34763
34789
  /* @__PURE__ */ jsx147("span", { className: "ods-toast__icon", "aria-hidden": "true", children: item.icon ?? defaultIcons[item.variant] }),
34764
- /* @__PURE__ */ jsxs141("div", { className: "ods-toast__body", children: [
34790
+ /* @__PURE__ */ jsxs141("div", { className: "ods-toast__body", onClick: onTextClick, children: [
34765
34791
  title && /* @__PURE__ */ jsx147("div", { className: "ods-toast__title", children: title }),
34766
- item.description && /* @__PURE__ */ jsx147("div", { className: "ods-toast__desc", children: item.description })
34792
+ item.description && /* @__PURE__ */ jsx147(
34793
+ "div",
34794
+ {
34795
+ ref: descRef,
34796
+ className: cn(
34797
+ "ods-toast__desc",
34798
+ cut && "ods-toast__desc--cut",
34799
+ toggles && "ods-toast__desc--toggles"
34800
+ ),
34801
+ style: cut ? { "--ods-toast-clamp": item.clampLines } : void 0,
34802
+ children: item.description
34803
+ }
34804
+ ),
34805
+ toggles && /* @__PURE__ */ jsx147(
34806
+ "button",
34807
+ {
34808
+ type: "button",
34809
+ className: "ods-toast__more",
34810
+ "aria-expanded": expanded,
34811
+ onClick: (e) => {
34812
+ e.stopPropagation();
34813
+ toggle();
34814
+ },
34815
+ children: expanded ? "Less" : "More"
34816
+ }
34817
+ )
34767
34818
  ] }),
34768
34819
  (item.actionLabel || item.action) && /* @__PURE__ */ jsx147("div", { className: "ods-toast__action", children: item.actionLabel ? (
34769
34820
  // First-class action chip — consistent styling, auto-dismiss
@@ -34811,10 +34862,17 @@ function ToastBody({ item, onDismiss }) {
34811
34862
  )
34812
34863
  ] });
34813
34864
  }
34814
- function PositionStack({ items, position, onDismiss, maxStack }) {
34865
+ function PositionStack({ items, position, onDismiss, onHold, onRelease, maxStack }) {
34815
34866
  const [hovered, setHovered] = useState71(false);
34867
+ const [focused, setFocused] = useState71(false);
34816
34868
  const [pinned, setPinned] = useState71(false);
34817
34869
  const expanded = hovered || pinned;
34870
+ const held = hovered || focused;
34871
+ const ids = useMemo37(() => items.map((t) => t.id), [items]);
34872
+ useEffect57(() => {
34873
+ if (held) onHold(ids);
34874
+ else onRelease(ids);
34875
+ }, [held, ids, onHold, onRelease]);
34818
34876
  const ordered = useMemo37(() => [...items].reverse(), [items]);
34819
34877
  const isBottom = position.startsWith("bottom");
34820
34878
  const dir = isBottom ? -1 : 1;
@@ -34842,6 +34900,10 @@ function PositionStack({ items, position, onDismiss, maxStack }) {
34842
34900
  ),
34843
34901
  onMouseEnter: () => setHovered(true),
34844
34902
  onMouseLeave: () => setHovered(false),
34903
+ onFocus: () => setFocused(true),
34904
+ onBlur: (e) => {
34905
+ if (!e.currentTarget.contains(e.relatedTarget)) setFocused(false);
34906
+ },
34845
34907
  "aria-live": "polite",
34846
34908
  children: [
34847
34909
  /* @__PURE__ */ jsx147(
@@ -34879,7 +34941,14 @@ function PositionStack({ items, position, onDismiss, maxStack }) {
34879
34941
  setPinned(true);
34880
34942
  }
34881
34943
  },
34882
- children: /* @__PURE__ */ jsx147(ToastBody, { item, onDismiss })
34944
+ children: /* @__PURE__ */ jsx147(
34945
+ ToastBody,
34946
+ {
34947
+ item,
34948
+ stacked: !expanded && items.length > 1,
34949
+ onDismiss
34950
+ }
34951
+ )
34883
34952
  },
34884
34953
  item.id
34885
34954
  );
@@ -34916,29 +34985,51 @@ function ToastProvider({
34916
34985
  const [toasts, setToasts] = useState71([]);
34917
34986
  const timers = useRef50(/* @__PURE__ */ new Map());
34918
34987
  const idCounter2 = useRef50(0);
34919
- const dismiss = useCallback51((id) => {
34920
- setToasts((prev) => prev.filter((t) => t.id !== id));
34921
- const timer = timers.current.get(id);
34922
- if (timer) {
34923
- clearTimeout(timer);
34924
- timers.current.delete(id);
34925
- }
34988
+ const forget = useCallback51((id) => {
34989
+ const c = timers.current.get(id);
34990
+ if (c?.handle) clearTimeout(c.handle);
34991
+ timers.current.delete(id);
34926
34992
  }, []);
34927
- const dismissAll = useCallback51((position) => {
34928
- setToasts((prev) => {
34929
- const remaining = position ? prev.filter((t) => t.position !== position) : [];
34930
- for (const t of prev) {
34931
- if (!remaining.find((r) => r.id === t.id)) {
34932
- const timer = timers.current.get(t.id);
34933
- if (timer) {
34934
- clearTimeout(timer);
34935
- timers.current.delete(t.id);
34936
- }
34993
+ const dismiss = useCallback51(
34994
+ (id) => {
34995
+ setToasts((prev) => prev.filter((t) => t.id !== id));
34996
+ forget(id);
34997
+ },
34998
+ [forget]
34999
+ );
35000
+ const dismissAll = useCallback51(
35001
+ (position) => {
35002
+ setToasts((prev) => {
35003
+ const remaining = position ? prev.filter((t) => t.position !== position) : [];
35004
+ for (const t of prev) {
35005
+ if (!remaining.find((r) => r.id === t.id)) forget(t.id);
34937
35006
  }
34938
- }
34939
- return remaining;
34940
- });
35007
+ return remaining;
35008
+ });
35009
+ },
35010
+ [forget]
35011
+ );
35012
+ const hold = useCallback51((ids) => {
35013
+ const now2 = Date.now();
35014
+ for (const id of ids) {
35015
+ const c = timers.current.get(id);
35016
+ if (!c || c.handle === null) continue;
35017
+ clearTimeout(c.handle);
35018
+ c.handle = null;
35019
+ c.remaining = Math.max(0, c.remaining - (now2 - c.since));
35020
+ }
34941
35021
  }, []);
35022
+ const release = useCallback51(
35023
+ (ids) => {
35024
+ for (const id of ids) {
35025
+ const c = timers.current.get(id);
35026
+ if (!c || c.handle !== null) continue;
35027
+ c.since = Date.now();
35028
+ c.handle = setTimeout(() => dismiss(id), c.remaining);
35029
+ }
35030
+ },
35031
+ [dismiss]
35032
+ );
34942
35033
  const toast = useCallback51(
34943
35034
  (options2) => {
34944
35035
  const id = `toast-${++idCounter2.current}`;
@@ -34955,12 +35046,16 @@ function ToastProvider({
34955
35046
  render: options2.render,
34956
35047
  duration: options2.duration ?? 5e3,
34957
35048
  closeable: options2.closeable ?? true,
34958
- position: options2.position ?? defaultPosition
35049
+ position: options2.position ?? defaultPosition,
35050
+ clampLines: options2.clampLines ?? 2
34959
35051
  };
34960
35052
  setToasts((prev) => [...prev, item]);
34961
35053
  if (item.duration > 0) {
34962
- const timer = setTimeout(() => dismiss(id), item.duration);
34963
- timers.current.set(id, timer);
35054
+ timers.current.set(id, {
35055
+ handle: setTimeout(() => dismiss(id), item.duration),
35056
+ remaining: item.duration,
35057
+ since: Date.now()
35058
+ });
34964
35059
  }
34965
35060
  return id;
34966
35061
  },
@@ -34988,7 +35083,9 @@ function ToastProvider({
34988
35083
  items: list,
34989
35084
  position: pos,
34990
35085
  maxStack,
34991
- onDismiss: dismiss
35086
+ onDismiss: dismiss,
35087
+ onHold: hold,
35088
+ onRelease: release
34992
35089
  },
34993
35090
  pos
34994
35091
  );
@@ -35840,7 +35937,7 @@ UserCard.displayName = "UserCard";
35840
35937
  import { useId as useId64, useState as useState74 } from "react";
35841
35938
 
35842
35939
  // src/hooks/useRelativeTime.ts
35843
- import { useEffect as useEffect57, useState as useState73 } from "react";
35940
+ import { useEffect as useEffect58, useState as useState73 } from "react";
35844
35941
  function toMs(t) {
35845
35942
  if (t == null) return null;
35846
35943
  return typeof t === "number" ? t : t.getTime();
@@ -35860,7 +35957,7 @@ function formatRelativeTime(when, now2 = Date.now()) {
35860
35957
  function useRelativeTime(date, options2 = {}) {
35861
35958
  const { intervalMs = 3e4 } = options2;
35862
35959
  const [now2, setNow] = useState73(() => Date.now());
35863
- useEffect57(() => {
35960
+ useEffect58(() => {
35864
35961
  if (typeof window === "undefined") return;
35865
35962
  const id = window.setInterval(() => setNow(Date.now()), intervalMs);
35866
35963
  return () => window.clearInterval(id);
@@ -35924,7 +36021,20 @@ function VersionHistory({
35924
36021
  }) {
35925
36022
  const [expandedId, setExpandedId] = useState74(defaultExpandedId);
35926
36023
  const baseId = useId64();
35927
- const showToolbar = Boolean(tabs && tabs.length > 0 || onRefresh);
36024
+ const showToolbar = Boolean(tabs && tabs.length > 0);
36025
+ const refreshButton = onRefresh ? /* @__PURE__ */ jsx155(
36026
+ "button",
36027
+ {
36028
+ type: "button",
36029
+ className: "ods-version-history__refresh",
36030
+ onClick: onRefresh,
36031
+ disabled: refreshing,
36032
+ "aria-label": "Refresh history",
36033
+ title: "Refresh",
36034
+ "data-spinning": refreshing || void 0,
36035
+ children: /* @__PURE__ */ jsx155(RefreshGlyph, {})
36036
+ }
36037
+ ) : null;
35928
36038
  return /* @__PURE__ */ jsxs149(
35929
36039
  SlideoutPanel,
35930
36040
  {
@@ -35933,6 +36043,7 @@ function VersionHistory({
35933
36043
  position,
35934
36044
  width,
35935
36045
  title,
36046
+ headerActions: showToolbar ? void 0 : refreshButton,
35936
36047
  offsetTop,
35937
36048
  offsetBottom,
35938
36049
  className: cn("ods-version-history", className),
@@ -35960,19 +36071,7 @@ function VersionHistory({
35960
36071
  tab.id
35961
36072
  );
35962
36073
  }) }) : /* @__PURE__ */ jsx155("span", {}),
35963
- onRefresh && /* @__PURE__ */ jsx155(
35964
- "button",
35965
- {
35966
- type: "button",
35967
- className: "ods-version-history__refresh",
35968
- onClick: onRefresh,
35969
- disabled: refreshing,
35970
- "aria-label": "Refresh history",
35971
- title: "Refresh",
35972
- "data-spinning": refreshing || void 0,
35973
- children: /* @__PURE__ */ jsx155(RefreshGlyph, {})
35974
- }
35975
- )
36074
+ refreshButton
35976
36075
  ] }),
35977
36076
  error && /* @__PURE__ */ jsx155("div", { className: "ods-version-history__banner", children: /* @__PURE__ */ jsx155(Banner, { variant: "error", appearance: "subtle", title: "Couldn't load history", description: error }) }),
35978
36077
  /* @__PURE__ */ jsx155("div", { className: "ods-version-history__body", role: "tabpanel", children: loading ? /* @__PURE__ */ jsxs149("div", { className: "ods-version-history__status", role: "status", "aria-live": "polite", children: [
@@ -37177,7 +37276,7 @@ import {
37177
37276
  } from "@octaviaflow/icons";
37178
37277
 
37179
37278
  // src/hooks/useWorkflowRuntime.ts
37180
- import { useEffect as useEffect58, useState as useState76 } from "react";
37279
+ import { useEffect as useEffect59, useState as useState76 } from "react";
37181
37280
  function toMs2(t) {
37182
37281
  if (t == null) return null;
37183
37282
  return typeof t === "number" ? t : t.getTime();
@@ -37192,7 +37291,7 @@ function formatWorkflowRuntime(ms) {
37192
37291
  }
37193
37292
  function useWorkflowRuntime(startedAt, active) {
37194
37293
  const [, force] = useState76(0);
37195
- useEffect58(() => {
37294
+ useEffect59(() => {
37196
37295
  if (!active || startedAt == null) return;
37197
37296
  if (typeof window === "undefined") return;
37198
37297
  const id = window.setInterval(() => force((n) => n + 1), 1e3);
@@ -37572,13 +37671,13 @@ function WorkflowHeaderExpanded({
37572
37671
  }
37573
37672
 
37574
37673
  // src/hooks/useBanner.ts
37575
- import { useCallback as useCallback56, useEffect as useEffect59, useRef as useRef52, useState as useState77 } from "react";
37674
+ import { useCallback as useCallback56, useEffect as useEffect60, useRef as useRef52, useState as useState77 } from "react";
37576
37675
  function useBanner(options2 = {}) {
37577
37676
  const { defaultOpen = false, autoDismissAfter, onDismiss } = options2;
37578
37677
  const [open, setOpen] = useState77(defaultOpen);
37579
37678
  const timerRef = useRef52(null);
37580
37679
  const onDismissRef = useRef52(onDismiss);
37581
- useEffect59(() => {
37680
+ useEffect60(() => {
37582
37681
  onDismissRef.current = onDismiss;
37583
37682
  }, [onDismiss]);
37584
37683
  const clearTimer = useCallback56(() => {
@@ -37609,7 +37708,7 @@ function useBanner(options2 = {}) {
37609
37708
  if (open) hide();
37610
37709
  else show();
37611
37710
  }, [open, show, hide]);
37612
- useEffect59(() => () => clearTimer(), [clearTimer]);
37711
+ useEffect60(() => () => clearTimer(), [clearTimer]);
37613
37712
  return {
37614
37713
  open,
37615
37714
  show,
@@ -37677,7 +37776,7 @@ function breakpointMinWidth(name) {
37677
37776
  }
37678
37777
 
37679
37778
  // src/hooks/useCallout.ts
37680
- import { useCallback as useCallback57, useEffect as useEffect60, useState as useState78 } from "react";
37779
+ import { useCallback as useCallback57, useEffect as useEffect61, useState as useState78 } from "react";
37681
37780
  var STORAGE_PREFIX = "ods:callout:dismissed:";
37682
37781
  function readPersisted(key) {
37683
37782
  if (!key || typeof window === "undefined") return false;
@@ -37701,7 +37800,7 @@ function writePersisted(key, value) {
37701
37800
  function useCallout(options2 = {}) {
37702
37801
  const { defaultOpen = true, persistKey, onDismiss } = options2;
37703
37802
  const [open, setOpen] = useState78(defaultOpen);
37704
- useEffect60(() => {
37803
+ useEffect61(() => {
37705
37804
  if (persistKey && readPersisted(persistKey)) {
37706
37805
  setOpen(false);
37707
37806
  }
@@ -39522,7 +39621,7 @@ NotificationChannelCard.displayName = "NotificationChannelCard";
39522
39621
 
39523
39622
  // src/notifications/components/NotificationChannelDialog/NotificationChannelDialog.tsx
39524
39623
  import { SendIcon as SendIcon2 } from "@octaviaflow/icons";
39525
- import { useEffect as useEffect61, useMemo as useMemo42, useState as useState86 } from "react";
39624
+ import { useEffect as useEffect62, useMemo as useMemo42, useState as useState86 } from "react";
39526
39625
 
39527
39626
  // src/notifications/engine/channels.ts
39528
39627
  var CONNECTED_CHANNEL_TYPES = [
@@ -39980,7 +40079,7 @@ function NotificationChannelDialog({
39980
40079
  const [testing, setTesting] = useState86(false);
39981
40080
  const [testResult, setTestResult] = useState86(null);
39982
40081
  const [submitError, setSubmitError] = useState86(null);
39983
- useEffect61(() => {
40082
+ useEffect62(() => {
39984
40083
  if (!open) return;
39985
40084
  setDraft(draftFromChannel(channel, initialType));
39986
40085
  setSubmitted(false);
@@ -40921,13 +41020,13 @@ NotificationRuleList.displayName = "NotificationRuleList";
40921
41020
 
40922
41021
  // src/provider/OdsProvider.tsx
40923
41022
  import { generateCssVars, resolveConfig } from "@octaviaflow/config";
40924
- import { createContext as createContext4, useContext as useContext5, useEffect as useEffect62, useMemo as useMemo45 } from "react";
41023
+ import { createContext as createContext4, useContext as useContext5, useEffect as useEffect63, useMemo as useMemo45 } from "react";
40925
41024
  import { OverlayProvider as OverlayProvider3 } from "react-aria";
40926
41025
  import { jsx as jsx173 } from "react/jsx-runtime";
40927
41026
  var OdsContext = createContext4(null);
40928
41027
  function OdsProvider({ config: userConfig, children }) {
40929
41028
  const resolved = useMemo45(() => resolveConfig(userConfig), [userConfig]);
40930
- useEffect62(() => {
41029
+ useEffect63(() => {
40931
41030
  const cssVarsBlock = generateCssVars(resolved);
40932
41031
  const match = cssVarsBlock.match(/:root\s*\{([\s\S]*)\}/);
40933
41032
  if (match) {