Skip to main content 首页 创作者 foolhardy45 portfolio molykit
molykit CRITICAL: Use for MolyKit AI chat toolkit. Triggers on:
BotClient, OpenAI, SSE streaming, AI chat, molykit,
PlatformSend, spawn(), ThreadToken, cross-platform async,
Chat widget, Messages, PromptInput, Avatar, LLM
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/foolhardy45/portfolio --skill molykit命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name molykit description CRITICAL: Use for MolyKit AI chat toolkit. Triggers on:
BotClient, OpenAI, SSE streaming, AI chat, molykit,
PlatformSend, spawn(), ThreadToken, cross-platform async,
Chat widget, Messages, PromptInput, Avatar, LLM
risk unknown source community
MolyKit Skill
Best practices for building AI chat interfaces with Makepad using MolyKit - a toolkit for cross-platform AI chat applications.
Source codebase : /Users/zhangalex/Work/Projects/FW/robius/moly/moly-kit
Triggers
Use this skill when:
Building AI chat interfaces with Makepad
Integrating OpenAI or other LLM APIs
Implementing cross-platform async for native and WASM
Creating chat widgets (messages, prompts, avatars)
Handling SSE streaming responses
Keywords: molykit, moly-kit, ai chat, bot client, openai makepad, chat widget, sse streaming
Overview
MolyKit provides:
Cross-platform async utilities (PlatformSend, spawn(), ThreadToken)
Ready-to-use chat widgets (Chat, Messages, PromptInput, Avatar)
BotClient trait for AI provider integration
OpenAI-compatible client with SSE streaming
Protocol types for messages, bots, and tool calls
MCP (Model Context Protocol) support
Cross-Platform Async Patterns
PlatformSend - Send Only on Native
pub trait PlatformSend : PlatformSendInner {}
pub type BoxPlatformSendFuture <'a , T> = Pin<Box <dyn PlatformSendFuture<Output = T> + 'a >>;
pub type BoxPlatformSendStream <'a , T> = Pin<Box <dyn PlatformSendStream<Item = T> + 'a >>;
Platform-Agnostic Spawning
pub fn spawn (fut: impl PlatformSendFuture <Output = ()> + 'static );
spawn (async move {
let result = fetch_data ().await ;
Cx::post_action (DataReady (result));
SignalToUI::set_ui_signal ();
});
Task Cancellation with AbortOnDropHandle
pub struct AbortOnDropHandle (AbortHandle);
#[rust]
task_handle: Option <AbortOnDropHandle>,
fn start_task (&mut self ) {
let (future, handle) = abort_on_drop (async move {
});
self .task_handle = Some (handle);
spawn (async move { let _ = future.await ; });
}
ThreadToken for Non-Send Types on WASM
pub struct ThreadToken <T: 'static >;
impl <T> ThreadToken<T> {
pub fn new (value: T) -> Self ;
pub fn peek <R>(&self , f: impl FnOnce (&T) -> R) -> R;
pub fn peek_mut <R>(&self , f: impl FnOnce (&mut T) -> R) -> R;
}
let token = ThreadToken::new (non_send_value);
spawn (async move {
token.peek (|value| {
});
});
BotClient Trait
Implementing AI Provider Integration pub trait BotClient : Send {
fn send (
&mut self ,
bot_id: &BotId,
messages: &[Message],
tools: &[Tool],
) -> BoxPlatformSendStream<'static , ClientResult<MessageContent>>;
fn bots (&self ) -> BoxPlatformSendFuture<'static , ClientResult<Vec <Bot>>>;
fn clone_box (&self ) -> Box <dyn BotClient>;
}
let client = OpenAIClient::new ("https://api.openai.com/v1" .into ());
client.set_key ("sk-..." )?;
let context = BotContext::from (client);
BotContext - Sharable Wrapper
pub struct BotContext (Arc<Mutex<InnerBotContext>>);
impl BotContext {
pub fn load (&mut self ) -> BoxPlatformSendFuture<ClientResult<()>>;
pub fn bots (&self ) -> Vec <Bot>;
pub fn get_bot (&self , id: &BotId) -> Option <Bot>;
pub fn client (&self ) -> Box <dyn BotClient>;
}
let mut context = BotContext::from (client);
spawn (async move {
if let Err (errors) = context.load ().await .into_result () {
}
Cx::post_action (BotsLoaded);
});
Protocol Types
Message Structure pub struct Message {
pub from: EntityId,
pub metadata: MessageMetadata,
pub content: MessageContent,
}
pub struct MessageContent {
pub text: String ,
pub reasoning: String ,
pub citations: Vec <String >,
pub attachments: Vec <Attachment>,
pub tool_calls: Vec <ToolCall>,
pub tool_results: Vec <ToolResult>,
}
pub struct MessageMetadata {
pub is_writing: bool ,
pub created_at: DateTime<Utc>,
}
Bot Identification
pub struct BotId (Arc<str >);
impl BotId {
pub fn new (id: &str , provider: &str ) -> Self ;
pub fn id (&self ) -> &str ;
pub fn provider (&self ) -> &str ;
}
Widget Patterns
Slot Widget - Runtime Content Replacement live_design! {
pub Slot = {{Slot}} {
width: Fill, height: Fit,
slot = <View> {}
}
}
let mut slot = widget.slot (id!(content));
if let Some (custom) = client.content_widget (cx, ...) {
slot.replace (custom);
} else {
slot.restore ();
slot.default ().as_standard_message_content ().set_content (cx, &content);
}
Avatar Widget - Text/Image Toggle live_design! {
pub Avatar = {{Avatar}} <View> {
grapheme = <RoundedView> {
visible: false ,
label = <Label> { text: "P" }
}
dependency = <RoundedView> {
visible: false ,
image = <Image> {}
}
}
}
impl Widget for Avatar {
fn draw_walk (&mut self , cx: &mut Cx2d, ...) -> DrawStep {
if let Some (avatar) = &self .avatar {
match avatar {
Picture::Grapheme (g) => {
self .view (id!(grapheme)).set_visible (cx, true );
self .view (id!(dependency)).set_visible (cx, false );
self .label (id!(label)).set_text (cx, &g);
}
Picture::Dependency (d) => {
self .view (id!(dependency)).set_visible (cx, true );
self .view (id!(grapheme)).set_visible (cx, false );
self .image (id!(image)).load_image_dep_by_path (cx, d.as_str ());
}
}
}
self .deref.draw_walk (cx, scope, walk)
}
}
PromptInput Widget #[derive(Live, Widget)]
pub struct PromptInput {
#[deref] deref: CommandTextInput,
#[live] pub send_icon: LiveValue,
#[live] pub stop_icon: LiveValue,
#[rust] pub task: Task,
#[rust] pub interactivity: Interactivity,
}
impl PromptInput {
pub fn submitted (&self , actions: &Actions) -> bool ;
pub fn reset (&mut self , cx: &mut Cx);
pub fn set_send (&mut self );
pub fn set_stop (&mut self );
pub fn enable (&mut self );
pub fn disable (&mut self );
}
Messages Widget - Conversation View #[derive(Live, Widget)]
pub struct Messages {
#[deref] deref: View,
#[rust] pub messages: Vec <Message>,
#[rust] pub bot_context: Option <BotContext>,
}
impl Messages {
pub fn set_messages (&mut self , messages: Vec <Message>, scroll_to_bottom: bool );
pub fn scroll_to_bottom (&mut self , cx: &mut Cx, triggered_by_stream: bool );
pub fn is_at_bottom (&self ) -> bool ;
}
UiRunner Pattern for Async-to-UI impl Widget for PromptInput {
fn handle_event (&mut self , cx: &mut Cx, event: &Event, scope: &mut Scope) {
self .deref.handle_event (cx, event, scope);
self .ui_runner ().handle (cx, event, scope, self );
if self .button (id!(attach)).clicked (event.actions ()) {
let ui = self .ui_runner ();
Attachment::pick_multiple (move |result| match result {
Ok (attachments) => {
ui.defer_with_redraw (move |me, cx, _| {
me.attachment_list_ref ().write ().attachments.extend (attachments);
});
}
Err (_) => {}
});
}
}
}
SSE Streaming
pub fn parse_sse <S, B, E>(s: S) -> impl Stream <Item = Result <String , E>>
where
S: Stream<Item = Result <B, E>>,
B: AsRef <[u8 ]>,
{
}
fn send (&mut self , ...) -> BoxPlatformSendStream<...> {
let stream = stream! {
let response = client.post (url).send ().await ?;
let events = parse_sse (response.bytes_stream ());
for await event in events {
let completion : Completion = serde_json::from_str (&event)?;
content.text.push_str (&completion.delta.content);
yield ClientResult::new_ok (content.clone ());
}
};
Box ::pin (stream)
}
Best Practices
Use PlatformSend for cross-platform : Same code works on native and WASM
Use spawn() not tokio::spawn : Platform-agnostic task spawning
Use AbortOnDropHandle : Cancel tasks when widget drops
Use ThreadToken for non-Send on WASM : Thread-local storage with token access
Use Slot for custom content : Allow BotClient to provide custom widgets
Use read()/write() pattern : Safe borrow access via WidgetRef
Use UiRunner::defer_with_redraw : Update widget from async context
Handle ClientResult partial success : May have value AND errors
Reference Files
llms.txt - Complete MolyKit API reference