@astrosheep/square 0.3.95 → 0.3.96
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/attention-presentation.d.ts +5 -4
- package/dist/attention-presentation.js +18 -8
- package/dist/boundary-presentation.js +17 -8
- package/dist/channel.d.ts +72 -0
- package/dist/channel.js +86 -0
- package/dist/claude-delivery.js +2 -1
- package/dist/delivery.js +2 -2
- package/dist/inbox.js +3 -1
- package/dist/mcp.js +65 -63
- package/dist/model.d.ts +6 -0
- package/dist/model.js +19 -0
- package/dist/ports.d.ts +3 -0
- package/dist/presentation-operations.d.ts +1 -0
- package/dist/sqlite-square.d.ts +3 -1
- package/dist/sqlite-square.js +54 -2
- package/dist/square-core.d.ts +1 -0
- package/dist/square-domain.js +22 -7
- package/dist/square-facade.d.ts +20 -2
- package/dist/square-sqlite.js +12 -3
- package/dist/square-tools.d.ts +1 -0
- package/dist/square-tools.js +9 -1
- package/dist/views.js +1 -0
- package/package.json +5 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/paseo/client/marble.ts +7 -0
- package/plugins/paseo/client/markdown.tsx +65 -9
- package/plugins/paseo/client/palette.ts +43 -20
- package/plugins/paseo/client/square-message.tsx +57 -48
- package/plugins/paseo/shared/message.ts +1 -2
- package/plugins/pi/square-pi.js +6 -4
|
@@ -1,95 +1,104 @@
|
|
|
1
1
|
import type { PluginTimelineItemProps } from "@getpaseo/plugin/client";
|
|
2
2
|
import { Icon } from "@getpaseo/plugin/client/react-native";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
|
|
4
|
+
import { Pressable, ScrollView, StyleSheet, Text, View, type ViewStyle } from "react-native";
|
|
5
5
|
import { parseSquareBlocks, parseSquareVoice, type SquareMessageData } from "../shared/message";
|
|
6
|
-
import { MONO, SquareInline, SquareMarkdownBody } from "./markdown";
|
|
6
|
+
import { MONO, SquareCue, SquareInline, SquareMarkdownBody } from "./markdown";
|
|
7
7
|
import { squarePalette } from "./palette";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The accent ■ marks the square itself, not a route.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Header (both states): ■ square · who spoke … act ids · how many.
|
|
12
|
+
* Collapsed: two preview lines. Expanded: the whole scene, at full width.
|
|
13
|
+
* One attribution style for one voice or many — each speaker's name runs into
|
|
14
|
+
* their first line in the seal's color (not repeated while they keep talking);
|
|
15
|
+
* a bell or an *action* is stage direction, a caption on a hairline.
|
|
15
16
|
*/
|
|
16
17
|
export function SquareMessage({ item, theme, layout }: PluginTimelineItemProps<SquareMessageData>) {
|
|
17
18
|
const [expanded, setExpanded] = useState(false);
|
|
19
|
+
const [hovered, setHovered] = useState(false);
|
|
18
20
|
const palette = useMemo(() => squarePalette(theme), [theme]);
|
|
19
21
|
const { text, items } = item.data;
|
|
20
22
|
const latest = items?.[items.length - 1];
|
|
21
23
|
const preview = latest?.preview?.replace(/\s+/g, " ").trim();
|
|
22
24
|
const blocks = useMemo(() => parseSquareBlocks(text), [text]);
|
|
23
|
-
const
|
|
24
|
-
const single = multi ? undefined : parseSquareVoice(blocks[0].actor, blocks[0].meta);
|
|
25
|
+
const voices = useMemo(() => blocks.map(block => parseSquareVoice(block.actor, block.meta)), [blocks]);
|
|
25
26
|
const names = items !== undefined
|
|
26
27
|
? items.map(entry => entry.actor)
|
|
27
|
-
:
|
|
28
|
+
: voices.map(voice => voice.speaker);
|
|
28
29
|
const speakers = [...new Set(names.filter((name): name is string => name !== undefined))];
|
|
29
30
|
const squareName = latest?.square ?? blocks[0].meta?.split("·")[1]?.trim() ?? "Square";
|
|
30
|
-
const bell = latest?.route === "bell" ||
|
|
31
|
+
const bell = latest?.route === "bell" || (voices.length === 1 && voices[0].bell);
|
|
31
32
|
// Header is the same in both states; act id is always shown when we know it.
|
|
32
|
-
const actIds = [...new Set(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}))];
|
|
36
|
-
const trailing = `· ${squareName}${actIds.length ? ` · ${actIds.join(", ")}` : ""}${items && items.length > 1 ? ` · ${items.length}` : ""}`;
|
|
33
|
+
const actIds = [...new Set(voices.flatMap(voice => voice.actId ? [voice.actId] : []))];
|
|
34
|
+
const trailing = [actSpan(actIds), items && items.length > 1 ? String(items.length) : undefined]
|
|
35
|
+
.filter(Boolean).join(" · ");
|
|
37
36
|
const { accent, border, foreground, foregroundMuted: muted, surface1 } = palette.theme.colors;
|
|
37
|
+
// On web the header brightens its details under the cursor; touch keeps them quiet.
|
|
38
|
+
const detail = layout.platform === "web" && hovered ? foreground : muted;
|
|
38
39
|
const row = { flexDirection: "row" as const, alignItems: "center" as const, gap: 6 };
|
|
39
40
|
const indent = { paddingLeft: 15 };
|
|
40
41
|
|
|
41
42
|
return (
|
|
42
|
-
<View style={
|
|
43
|
+
<View style={[
|
|
44
|
+
{ backgroundColor: surface1, borderWidth: StyleSheet.hairlineWidth, borderColor: border, borderRadius: 4, paddingHorizontal: 12, paddingVertical: 9 },
|
|
45
|
+
layout.platform === "web" && palette.stone(`${squareName} ${actIds[0] ?? ""}`) as ViewStyle,
|
|
46
|
+
]}>
|
|
43
47
|
<Pressable
|
|
44
48
|
accessibilityRole="button"
|
|
45
49
|
accessibilityLabel={`${expanded ? "收起" : "展开"} ${squareName}: ${speakers.join(", ") || "Square"}`}
|
|
46
50
|
aria-expanded={expanded}
|
|
47
51
|
onPress={() => setExpanded(value => !value)}
|
|
48
|
-
|
|
52
|
+
onHoverIn={() => setHovered(true)}
|
|
53
|
+
onHoverOut={() => setHovered(false)}
|
|
54
|
+
style={({ pressed }) => ({ opacity: pressed ? 0.85 : 1 })}
|
|
49
55
|
>
|
|
50
56
|
<View style={row}>
|
|
51
|
-
<
|
|
52
|
-
<Text numberOfLines={1} style={{ color: foreground, fontSize: 13, fontWeight: "
|
|
53
|
-
{
|
|
54
|
-
{
|
|
57
|
+
<View style={[{ width: 9, height: 9, borderRadius: 1.5, backgroundColor: accent }, layout.platform === "web" && palette.inlay as ViewStyle]} />
|
|
58
|
+
<Text numberOfLines={1} style={{ color: foreground, fontSize: 13, fontWeight: "600", flex: 1 }}>
|
|
59
|
+
{squareName}
|
|
60
|
+
{(speakers.length > 0 || bell) && (
|
|
61
|
+
<Text style={{ color: muted, fontWeight: "400" }}> · {[speakers.join(", "), bell ? "🔔" : ""].filter(Boolean).join(" · ")}</Text>
|
|
62
|
+
)}
|
|
55
63
|
</Text>
|
|
56
|
-
<Text style={{ color:
|
|
57
|
-
<Icon name={expanded ? "ChevronUp" : "ChevronDown"} size={15} color={
|
|
64
|
+
{trailing !== "" && <Text style={{ color: detail, fontSize: 11, fontFamily: MONO, fontVariant: ["tabular-nums"] }}>{trailing}</Text>}
|
|
65
|
+
<Icon name={expanded ? "ChevronUp" : "ChevronDown"} size={15} color={detail} />
|
|
58
66
|
</View>
|
|
59
67
|
{!expanded && preview !== undefined && preview !== "" && (
|
|
60
68
|
<View style={{ ...indent, marginTop: 3 }}>
|
|
61
|
-
<SquareInline text={preview} theme={palette.theme} numberOfLines={2} />
|
|
69
|
+
<SquareInline text={preview} theme={palette.theme} numberOfLines={2} lead={latest?.actor} />
|
|
62
70
|
</View>
|
|
63
71
|
)}
|
|
64
72
|
</Pressable>
|
|
65
73
|
{expanded && (
|
|
66
74
|
<ScrollView style={{ maxHeight: layout.compact ? 280 : 400, marginTop: 3 }} nestedScrollEnabled>
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
})}
|
|
87
|
-
</View>
|
|
88
|
-
) : (
|
|
89
|
-
<SquareMarkdownBody text={blocks[0].body} theme={palette.theme} style={indent} />
|
|
90
|
-
)}
|
|
75
|
+
<View style={{ ...indent, gap: 7 }}>
|
|
76
|
+
{blocks.map((block, index) => {
|
|
77
|
+
const voice = voices[index];
|
|
78
|
+
if (block.body === "" && !voice.bell) return null;
|
|
79
|
+
const repeat = index > 0 && voices[index - 1].speaker === voice.speaker;
|
|
80
|
+
return (
|
|
81
|
+
<View key={voice.actId ?? index} style={{ gap: 6 }}>
|
|
82
|
+
{voice.bell && <SquareCue theme={palette.theme} label={`${voice.speaker ?? ""} 🔔`} />}
|
|
83
|
+
<SquareMarkdownBody
|
|
84
|
+
text={block.body}
|
|
85
|
+
theme={palette.theme}
|
|
86
|
+
lead={repeat ? undefined : voice.speaker}
|
|
87
|
+
actor={voice.speaker}
|
|
88
|
+
/>
|
|
89
|
+
</View>
|
|
90
|
+
);
|
|
91
|
+
})}
|
|
92
|
+
</View>
|
|
91
93
|
</ScrollView>
|
|
92
94
|
)}
|
|
93
95
|
</View>
|
|
94
96
|
);
|
|
95
97
|
}
|
|
98
|
+
|
|
99
|
+
/** act/18, act/19 → act/18–19; anything else is listed as is. */
|
|
100
|
+
function actSpan(ids: string[]): string | undefined {
|
|
101
|
+
if (ids.length < 2) return ids[0];
|
|
102
|
+
const numbers = ids.map(id => /^act\/(\d+)$/.exec(id)?.[1]).map(Number);
|
|
103
|
+
return numbers.every(Number.isInteger) ? `act/${Math.min(...numbers)}–${Math.max(...numbers)}` : ids.join(", ");
|
|
104
|
+
}
|
|
@@ -50,7 +50,7 @@ export function parseSquareBlocks(text: string): SquareBlock[] {
|
|
|
50
50
|
return blocks;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export interface SquareVoice {
|
|
53
|
+
export interface SquareVoice { speaker?: string; bell: boolean; actId?: string }
|
|
54
54
|
|
|
55
55
|
// A voice line is chrome for the card: `● rei rang the bell` names who, how, and which
|
|
56
56
|
// activity. Route prose is parsed into those marks, never rendered as narration.
|
|
@@ -58,7 +58,6 @@ export function parseSquareVoice(actorLine?: string, metaLine?: string): SquareV
|
|
|
58
58
|
const line = actorLine?.trim() ?? "";
|
|
59
59
|
const cleaned = line.replace(/^[●○◎◆■·]\s*/, "").trim();
|
|
60
60
|
return {
|
|
61
|
-
glyph: /^[●○◎·]/.exec(line)?.[0] ?? "●",
|
|
62
61
|
speaker: /\S+/.exec(cleaned)?.[0],
|
|
63
62
|
bell: /rang the bell/i.test(cleaned),
|
|
64
63
|
actId: /act\/\d+/.exec(metaLine ?? "")?.[0],
|
package/plugins/pi/square-pi.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Container, Markdown, Spacer, Text } from '@earendil-works/pi-tui';
|
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
|
|
5
5
|
import { Square, harnessOwner } from '../../dist/square-api.js';
|
|
6
|
-
import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, squareToolFailure, squareToolFailureText } from '../../dist/square-tools.js';
|
|
6
|
+
import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, squareToolsEnabled, squareToolFailure, squareToolFailureText } from '../../dist/square-tools.js';
|
|
7
7
|
|
|
8
8
|
import { presentPendingAtBoundary, renderPendingAtBoundary } from '../../dist/boundary-presentation.js';
|
|
9
9
|
import { operationEnv } from '../../dist/participant-identity.js';
|
|
@@ -46,8 +46,10 @@ export function pendingCardDetails(pending) {
|
|
|
46
46
|
recipient: membership.seat.participant,
|
|
47
47
|
route: note.route,
|
|
48
48
|
act: formatActivityId(note.actIndex),
|
|
49
|
-
preview: previewAttentionBody(note.body),
|
|
50
|
-
description:
|
|
49
|
+
preview: previewAttentionBody(note.body, note.delivery),
|
|
50
|
+
description: note.delivery?.mode === 'direct'
|
|
51
|
+
? `${note.actor} → ${membership.seat.participant}`
|
|
52
|
+
: renderAttentionDescription({ actor: note.actor, route: note.route }),
|
|
51
53
|
body: note.body,
|
|
52
54
|
})));
|
|
53
55
|
if (items.length === 0) return undefined;
|
|
@@ -141,7 +143,7 @@ export function renderSquareCard(message, { expanded, outputPad = 1 }, theme) {
|
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
export default function squarePiExtension(pi) {
|
|
144
|
-
for (const tool of SQUARE_TOOLS) {
|
|
146
|
+
if (squareToolsEnabled(process.env)) for (const tool of SQUARE_TOOLS) {
|
|
145
147
|
pi.registerTool({
|
|
146
148
|
name: `square_${tool.name}`,
|
|
147
149
|
label: `Square ${tool.name}`,
|