UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

360 lines (359 loc) โ€ข 7.69 kB
//#region extensions/line/src/flex-templates/common.ts function attachFooterText(bubble, footer) { bubble.footer = { type: "box", layout: "vertical", contents: [{ type: "text", text: footer, size: "xs", color: "#AAAAAA", wrap: true, align: "center" }], paddingAll: "lg", backgroundColor: "#FAFAFA" }; } //#endregion //#region extensions/line/src/flex-templates/schedule-cards.ts function buildTitleSubtitleHeader(params) { const { title, subtitle } = params; const headerContents = [{ type: "text", text: title, weight: "bold", size: "xl", color: "#111111", wrap: true }]; if (subtitle) headerContents.push({ type: "text", text: subtitle, size: "sm", color: "#888888", margin: "sm", wrap: true }); return headerContents; } function buildCardHeaderSections(headerContents) { return [{ type: "box", layout: "vertical", contents: headerContents, paddingBottom: "lg" }, { type: "separator", color: "#EEEEEE" }]; } function createMegaBubbleWithFooter(params) { const bubble = { type: "bubble", size: "mega", body: { type: "box", layout: "vertical", contents: params.bodyContents, paddingAll: "xl", backgroundColor: "#FFFFFF" } }; if (params.footer) attachFooterText(bubble, params.footer); return bubble; } /** * Create a receipt/summary card (for orders, transactions, data tables) * * Editorial design: Clean table layout with alternating row backgrounds, * prominent total section, and clear visual hierarchy. */ function createReceiptCard(params) { const { title, subtitle, items, total, footer } = params; const itemRows = items.slice(0, 12).map((item, index) => ({ type: "box", layout: "horizontal", contents: [{ type: "text", text: item.name, size: "sm", color: item.highlight ? "#111111" : "#666666", weight: item.highlight ? "bold" : "regular", flex: 3, wrap: true }, { type: "text", text: item.value, size: "sm", color: item.highlight ? "#06C755" : "#333333", weight: item.highlight ? "bold" : "regular", flex: 2, align: "end", wrap: true }], paddingAll: "md", backgroundColor: index % 2 === 0 ? "#FFFFFF" : "#FAFAFA" })); const bodyContents = [...buildCardHeaderSections(buildTitleSubtitleHeader({ title, subtitle })), { type: "box", layout: "vertical", contents: itemRows, margin: "md", cornerRadius: "md", borderWidth: "light", borderColor: "#EEEEEE" }]; if (total) bodyContents.push({ type: "box", layout: "horizontal", contents: [{ type: "text", text: total.label, size: "lg", weight: "bold", color: "#111111", flex: 2 }, { type: "text", text: total.value, size: "xl", weight: "bold", color: "#06C755", flex: 2, align: "end" }], margin: "xl", paddingAll: "lg", backgroundColor: "#F0FDF4", cornerRadius: "lg" }); return createMegaBubbleWithFooter({ bodyContents, footer }); } /** * Create a calendar event card (for meetings, appointments, reminders) * * Editorial design: Date as hero, strong typographic hierarchy, * color-blocked zones, full text wrapping for readability. */ function createEventCard(params) { const { title, date, time, location, description, calendar, isAllDay, action } = params; const dateBlock = { type: "box", layout: "vertical", contents: [{ type: "text", text: date.toUpperCase(), size: "sm", weight: "bold", color: "#06C755", wrap: true }, { type: "text", text: isAllDay ? "ALL DAY" : time ?? "", size: "xxl", weight: "bold", color: "#111111", wrap: true, margin: "xs" }], paddingBottom: "lg", borderWidth: "none" }; if (!time && !isAllDay) dateBlock.contents = [{ type: "text", text: date, size: "xl", weight: "bold", color: "#111111", wrap: true }]; const bodyContents = [dateBlock, { type: "box", layout: "horizontal", contents: [{ type: "box", layout: "vertical", contents: [], width: "4px", backgroundColor: "#06C755", cornerRadius: "2px" }, { type: "box", layout: "vertical", contents: [{ type: "text", text: title, size: "lg", weight: "bold", color: "#1a1a1a", wrap: true }, ...calendar ? [{ type: "text", text: calendar, size: "xs", color: "#888888", margin: "sm", wrap: true }] : []], flex: 1, paddingStart: "lg" }], paddingTop: "lg", paddingBottom: "lg", borderWidth: "light", borderColor: "#EEEEEE" }]; if (location || description) { const detailItems = []; if (location) detailItems.push({ type: "box", layout: "horizontal", contents: [{ type: "text", text: "๐Ÿ“", size: "sm", flex: 0 }, { type: "text", text: location, size: "sm", color: "#444444", margin: "md", flex: 1, wrap: true }], alignItems: "flex-start" }); if (description) detailItems.push({ type: "text", text: description, size: "sm", color: "#666666", wrap: true, margin: location ? "lg" : "none" }); bodyContents.push({ type: "box", layout: "vertical", contents: detailItems, margin: "lg", paddingAll: "lg", backgroundColor: "#F8F9FA", cornerRadius: "lg" }); } return { type: "bubble", size: "mega", body: { type: "box", layout: "vertical", contents: bodyContents, paddingAll: "xl", backgroundColor: "#FFFFFF", action } }; } /** * Create a calendar agenda card showing multiple events * * Editorial timeline design: Time-focused left column with event details * on the right. Visual accent bars indicate event priority/recency. */ function createAgendaCard(params) { const { title, subtitle, events, footer } = params; const headerContents = buildTitleSubtitleHeader({ title, subtitle }); const eventItems = events.slice(0, 6).map((event, index) => { const isActive = event.isNow || index === 0; const accentColor = isActive ? "#06C755" : "#E5E5E5"; const timeColumn = { type: "box", layout: "vertical", contents: [{ type: "text", text: event.time ?? "โ€”", size: "sm", weight: isActive ? "bold" : "regular", color: isActive ? "#06C755" : "#666666", align: "end", wrap: true }], width: "65px", justifyContent: "flex-start" }; const dotColumn = { type: "box", layout: "vertical", contents: [{ type: "box", layout: "vertical", contents: [], width: "10px", height: "10px", backgroundColor: accentColor, cornerRadius: "5px" }], width: "24px", alignItems: "center", justifyContent: "flex-start", paddingTop: "xs" }; const detailContents = [{ type: "text", text: event.title, size: "md", weight: "bold", color: "#1a1a1a", wrap: true }]; const secondaryParts = []; if (event.location) secondaryParts.push(event.location); if (event.calendar) secondaryParts.push(event.calendar); if (secondaryParts.length > 0) detailContents.push({ type: "text", text: secondaryParts.join(" ยท "), size: "xs", color: "#888888", wrap: true, margin: "xs" }); return { type: "box", layout: "horizontal", contents: [ timeColumn, dotColumn, { type: "box", layout: "vertical", contents: detailContents, flex: 1 } ], margin: index > 0 ? "xl" : void 0, alignItems: "flex-start" }; }); return createMegaBubbleWithFooter({ bodyContents: [...buildCardHeaderSections(headerContents), { type: "box", layout: "vertical", contents: eventItems, paddingTop: "xl" }], footer }); } //#endregion export { attachFooterText as i, createEventCard as n, createReceiptCard as r, createAgendaCard as t };