@tightknitai/block-kitchen 0.8.11 → 0.9.1
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/README.md +7 -5
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +91 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +92 -31
- package/dist/index.js.map +1 -1
- package/dist/{palette-Bup05opK.d.cts → palette-DIbvAzRA.d.cts} +12 -8
- package/dist/{palette-Bup05opK.d.ts → palette-DIbvAzRA.d.ts} +12 -8
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ export function MyBuilderPage() {
|
|
|
108
108
|
| `loadChannels` | `() => Promise<{ id: string; name: string }[]>` | yes | Returns channels available to send to. The package never makes Slack API calls itself. |
|
|
109
109
|
| `loadSendAsUserStatus` | `() => Promise<{ canSendAsUser: boolean; oauthUrl?: string }>` | yes | Whether the current user has a Slack user-token and can post as themselves. If `canSendAsUser` is false, `oauthUrl` is shown as a "Sign in with Slack" link. |
|
|
110
110
|
| `onSend` | `(payload) => Promise<{ ok: boolean; error?: string }>` | yes | Called when the user submits the send dialog. Payload is `{ channelId, blocks, sendAsUser }`. |
|
|
111
|
-
| `editing` | `{ onLoadMessage, onUpdate, loadRecentMessages? }` | no | Opt-in edit mode. When present, the toolbar exposes "Edit message": the user pastes a Slack message link, `onLoadMessage({ link })` returns a host-computed [editability verdict](#editing-an-existing-message-opt-in), and a successful load flips the primary action to "Update message" wired to `onUpdate`. Pass `loadRecentMessages` to add a "recent messages from this app" picker beside the paste input. Omit `editing` to keep send-only behavior. |
|
|
111
|
+
| `editing` | `{ onLoadMessage, onUpdate, loadRecentMessages? }` | no | Opt-in edit mode. When present, the toolbar exposes "Edit message": the user pastes a Slack message link, `onLoadMessage({ link })` returns a host-computed [editability verdict](#editing-an-existing-message-opt-in), and a successful load flips the primary action to "Update message" wired to `onUpdate`. Pass `loadRecentMessages` to add a "recent messages from this app" picker beside the paste input; the user picks a channel first (reusing `loadChannels`) and the lookup is scoped to it. Omit `editing` to keep send-only behavior. |
|
|
112
112
|
| `loadButtonLabel` | `string` | no | Label + accessible name for the toolbar button that opens the load-message dialog (the edit-mode entry point). Defaults to `'Load message'`. Only shown when `editing` is set and no message is loaded. |
|
|
113
113
|
| `updateButtonLabel` | `string` | no | Label for the toolbar's primary button while a message is loaded for editing. It's a split button: clicking it updates the message in place; the menu beside it also offers "Send as a new message" (post the current blocks as new). Defaults to `'Review & update'`. |
|
|
114
114
|
| `confirmUpdateLabel` | `string` | no | Label for the update dialog's final confirm button. Defaults to `'Update message'` (shows `'Updating…'` while in flight). |
|
|
@@ -162,10 +162,12 @@ nothing about who can edit; the host does both.
|
|
|
162
162
|
return { ok: true };
|
|
163
163
|
},
|
|
164
164
|
// Optional: adds a "recent messages from this app" picker beside the paste
|
|
165
|
-
// input.
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
// input. The user first picks a channel (reusing `loadChannels`), then this
|
|
166
|
+
// is called with that `channelId` so the lookup scans only one channel.
|
|
167
|
+
// These are editable-by-construction (the app authored them), so picking one
|
|
168
|
+
// loads it straight into edit mode (no verdict needed).
|
|
169
|
+
loadRecentMessages: async (channelId) => {
|
|
170
|
+
const msgs = await fetchRecentAppMessages(channelId); // your code
|
|
169
171
|
return msgs.map((m) => ({
|
|
170
172
|
channelId: m.channel,
|
|
171
173
|
channelName: m.channelName,
|
package/dist/helpers.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as SupportedBlock } from './palette-
|
|
2
|
-
export { U as SupportedBlockType } from './palette-
|
|
1
|
+
import { T as SupportedBlock } from './palette-DIbvAzRA.cjs';
|
|
2
|
+
export { U as SupportedBlockType } from './palette-DIbvAzRA.cjs';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'slack-web-api-client';
|
|
5
5
|
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as SupportedBlock } from './palette-
|
|
2
|
-
export { U as SupportedBlockType } from './palette-
|
|
1
|
+
import { T as SupportedBlock } from './palette-DIbvAzRA.js';
|
|
2
|
+
export { U as SupportedBlockType } from './palette-DIbvAzRA.js';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'slack-web-api-client';
|
|
5
5
|
|
package/dist/index.cjs
CHANGED
|
@@ -2851,19 +2851,25 @@ function LoadMessageDialog({
|
|
|
2851
2851
|
onOpenChange,
|
|
2852
2852
|
onLoadMessage,
|
|
2853
2853
|
loadRecentMessages,
|
|
2854
|
+
loadChannels,
|
|
2854
2855
|
onLoaded,
|
|
2855
2856
|
onOpenAsNew
|
|
2856
2857
|
}) {
|
|
2857
2858
|
const [link, setLink] = React.useState("");
|
|
2858
2859
|
const [status, setStatus] = React.useState({ kind: "idle" });
|
|
2860
|
+
const [channels, setChannels] = React.useState(null);
|
|
2861
|
+
const [channelsError, setChannelsError] = React.useState(null);
|
|
2862
|
+
const [channelId, setChannelId] = React.useState("");
|
|
2859
2863
|
const [recent, setRecent] = React.useState(null);
|
|
2860
2864
|
const [recentError, setRecentError] = React.useState(null);
|
|
2861
2865
|
const inputRef = React.useRef(null);
|
|
2862
2866
|
const onLoadMessageRef = React.useRef(onLoadMessage);
|
|
2863
2867
|
const loadRecentMessagesRef = React.useRef(loadRecentMessages);
|
|
2868
|
+
const loadChannelsRef = React.useRef(loadChannels);
|
|
2864
2869
|
React.useEffect(() => {
|
|
2865
2870
|
onLoadMessageRef.current = onLoadMessage;
|
|
2866
2871
|
loadRecentMessagesRef.current = loadRecentMessages;
|
|
2872
|
+
loadChannelsRef.current = loadChannels;
|
|
2867
2873
|
});
|
|
2868
2874
|
const hasRecent = !!loadRecentMessages;
|
|
2869
2875
|
React.useEffect(() => {
|
|
@@ -2872,15 +2878,38 @@ function LoadMessageDialog({
|
|
|
2872
2878
|
}
|
|
2873
2879
|
setLink("");
|
|
2874
2880
|
setStatus({ kind: "idle" });
|
|
2881
|
+
setChannelId("");
|
|
2882
|
+
setRecent(null);
|
|
2883
|
+
setRecentError(null);
|
|
2875
2884
|
if (!loadRecentMessagesRef.current) {
|
|
2876
|
-
|
|
2877
|
-
|
|
2885
|
+
setChannels([]);
|
|
2886
|
+
setChannelsError(null);
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
setChannels(null);
|
|
2890
|
+
setChannelsError(null);
|
|
2891
|
+
let cancelled = false;
|
|
2892
|
+
loadChannelsRef.current().then((list) => {
|
|
2893
|
+
if (!cancelled) {
|
|
2894
|
+
setChannels(list);
|
|
2895
|
+
}
|
|
2896
|
+
}).catch((e) => {
|
|
2897
|
+
if (!cancelled) {
|
|
2898
|
+
setChannelsError(e instanceof Error ? e.message : "Failed to load channels");
|
|
2899
|
+
}
|
|
2900
|
+
});
|
|
2901
|
+
return () => {
|
|
2902
|
+
cancelled = true;
|
|
2903
|
+
};
|
|
2904
|
+
}, [open]);
|
|
2905
|
+
React.useEffect(() => {
|
|
2906
|
+
if (!open || !loadRecentMessagesRef.current || !channelId) {
|
|
2878
2907
|
return;
|
|
2879
2908
|
}
|
|
2880
2909
|
setRecent(null);
|
|
2881
2910
|
setRecentError(null);
|
|
2882
2911
|
let cancelled = false;
|
|
2883
|
-
loadRecentMessagesRef.current().then((list) => {
|
|
2912
|
+
loadRecentMessagesRef.current(channelId).then((list) => {
|
|
2884
2913
|
if (!cancelled) {
|
|
2885
2914
|
setRecent(list);
|
|
2886
2915
|
}
|
|
@@ -2892,7 +2921,7 @@ function LoadMessageDialog({
|
|
|
2892
2921
|
return () => {
|
|
2893
2922
|
cancelled = true;
|
|
2894
2923
|
};
|
|
2895
|
-
}, [open]);
|
|
2924
|
+
}, [open, channelId]);
|
|
2896
2925
|
const handleLoad = async () => {
|
|
2897
2926
|
const trimmed = link.trim();
|
|
2898
2927
|
if (!trimmed) {
|
|
@@ -2996,10 +3025,33 @@ function LoadMessageDialog({
|
|
|
2996
3025
|
"or pick a recent message",
|
|
2997
3026
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })
|
|
2998
3027
|
] }),
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3028
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3029
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "recent-channel-picker", children: "Channel" }),
|
|
3030
|
+
channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
|
|
3031
|
+
channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
|
|
3032
|
+
channels && channels.length === 0 && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
|
|
3033
|
+
channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3034
|
+
"select",
|
|
3035
|
+
{
|
|
3036
|
+
id: "recent-channel-picker",
|
|
3037
|
+
value: channelId,
|
|
3038
|
+
onChange: (e) => setChannelId(e.target.value),
|
|
3039
|
+
className: "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
3040
|
+
children: [
|
|
3041
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a channel\u2026" }),
|
|
3042
|
+
channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
|
|
3043
|
+
"#",
|
|
3044
|
+
c.name
|
|
3045
|
+
] }, c.id))
|
|
3046
|
+
]
|
|
3047
|
+
}
|
|
3048
|
+
)
|
|
3049
|
+
] }),
|
|
3050
|
+
!channelId && channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Select a channel to see recent messages." }),
|
|
3051
|
+
channelId && recent === null && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading recent messages\u2026" }),
|
|
3052
|
+
channelId && recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: recentError }),
|
|
3053
|
+
channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
|
|
3054
|
+
channelId && recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
|
|
3003
3055
|
const asUser = (m.editableVia ?? "bot") === "user";
|
|
3004
3056
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3005
3057
|
"button",
|
|
@@ -3013,7 +3065,15 @@ function LoadMessageDialog({
|
|
|
3013
3065
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", children: m.channelName ? `#${m.channelName}` : m.channelId }),
|
|
3014
3066
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: asUser ? "You" : "Bot" })
|
|
3015
3067
|
] }),
|
|
3016
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3068
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
|
|
3069
|
+
new Date(Number(m.ts) * 1e3).toLocaleString(),
|
|
3070
|
+
" ",
|
|
3071
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
3072
|
+
"(",
|
|
3073
|
+
m.ts,
|
|
3074
|
+
")"
|
|
3075
|
+
] })
|
|
3076
|
+
] })
|
|
3017
3077
|
] }),
|
|
3018
3078
|
m.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-muted-foreground", children: m.label })
|
|
3019
3079
|
]
|
|
@@ -3327,25 +3387,6 @@ function SendDialog({
|
|
|
3327
3387
|
/* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Pick a channel and choose who to post as." })
|
|
3328
3388
|
] }),
|
|
3329
3389
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
3330
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3331
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "channel-picker", children: "Channel" }),
|
|
3332
|
-
channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
|
|
3333
|
-
channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
|
|
3334
|
-
channels && channels.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
|
|
3335
|
-
channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3336
|
-
"select",
|
|
3337
|
-
{
|
|
3338
|
-
id: "channel-picker",
|
|
3339
|
-
value: channelId,
|
|
3340
|
-
onChange: (e) => setChannelId(e.target.value),
|
|
3341
|
-
className: "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
3342
|
-
children: channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
|
|
3343
|
-
"#",
|
|
3344
|
-
c.name
|
|
3345
|
-
] }, c.id))
|
|
3346
|
-
}
|
|
3347
|
-
)
|
|
3348
|
-
] }),
|
|
3349
3390
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3350
3391
|
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "send-as-picker", children: "Post as" }),
|
|
3351
3392
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -3382,6 +3423,25 @@ function SendDialog({
|
|
|
3382
3423
|
"to post as yourself."
|
|
3383
3424
|
] })
|
|
3384
3425
|
] }),
|
|
3426
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3427
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "channel-picker", children: "Channel" }),
|
|
3428
|
+
channels === null && !channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Loading channels\u2026" }),
|
|
3429
|
+
channelsError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: channelsError }),
|
|
3430
|
+
channels && channels.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No public channels available." }),
|
|
3431
|
+
channels && channels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3432
|
+
"select",
|
|
3433
|
+
{
|
|
3434
|
+
id: "channel-picker",
|
|
3435
|
+
value: channelId,
|
|
3436
|
+
onChange: (e) => setChannelId(e.target.value),
|
|
3437
|
+
className: "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
3438
|
+
children: channels.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.id, children: [
|
|
3439
|
+
"#",
|
|
3440
|
+
c.name
|
|
3441
|
+
] }, c.id))
|
|
3442
|
+
}
|
|
3443
|
+
)
|
|
3444
|
+
] }),
|
|
3385
3445
|
errorCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3386
3446
|
"button",
|
|
3387
3447
|
{
|
|
@@ -9149,8 +9209,8 @@ function Toolbar2({
|
|
|
9149
9209
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9150
9210
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-1 border-b bg-background px-2 py-1.5 sm:px-3 sm:py-2", children: [
|
|
9151
9211
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [
|
|
9152
|
-
editingEnabled
|
|
9153
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
9212
|
+
editingEnabled ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", size: "sm", onClick: onOpenLoad, "aria-label": loadButtonLabel, children: [
|
|
9213
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MailSearch, { className: "h-3.5 w-3.5" }),
|
|
9154
9214
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden md:inline", children: loadButtonLabel })
|
|
9155
9215
|
] }) : null,
|
|
9156
9216
|
onOpenPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -9840,6 +9900,7 @@ function BlockKitchen(props) {
|
|
|
9840
9900
|
onOpenChange: setLoadOpen,
|
|
9841
9901
|
onLoadMessage: editing.onLoadMessage,
|
|
9842
9902
|
loadRecentMessages: editing.loadRecentMessages,
|
|
9903
|
+
loadChannels,
|
|
9843
9904
|
onLoaded: (result) => {
|
|
9844
9905
|
replaceAll(result.blocks);
|
|
9845
9906
|
setEditTarget({
|