Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions crates/flynt-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ fn default_project_root() -> PathBuf {
.join("Flynt")
}

fn is_transport_disconnect_message(msg: &str) -> bool {
let msg = msg.to_lowercase();
msg.contains("broken pipe")
|| msg.contains("os error 32")
|| msg.contains("connection closed")
|| msg.contains("closed connection")
|| msg.contains("connection reset")
|| msg.contains("transport disconnected")
|| msg.contains("channel closed")
}

fn is_transport_disconnect_error(err: &omegon_extension::Error) -> bool {
is_transport_disconnect_message(&err.to_string())
}

#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
Expand All @@ -54,8 +69,8 @@ async fn main() -> Result<()> {
"FLYNT_PROJECT",
&["OMEGON_PROJECT_ROOT", "FLYNT_VAULT", "CODEX_VAULT"],
)
.map(PathBuf::from)
.unwrap_or_else(default_project_root);
.map(PathBuf::from)
.unwrap_or_else(default_project_root);

std::fs::create_dir_all(&project_root)?;
let project = Arc::new(Project::open(&project_root)?);
Expand All @@ -67,9 +82,13 @@ async fn main() -> Result<()> {
match mode {
Some("--mcp") => {
// MCP server mode — compatible with Claude Code, Cursor, etc.
omegon_extension::mcp_shim::serve_mcp(ext)
.await
.expect("flynt MCP server failed");
if let Err(e) = omegon_extension::mcp_shim::serve_mcp(ext).await {
if is_transport_disconnect_error(&e) {
tracing::warn!("flynt MCP client disconnected: {e}");
return Ok(());
}
return Err(anyhow::anyhow!("flynt MCP server failed: {e}"));
}
}
Some("--help") | Some("help") | Some("-h") => {
println!("flynt-agent — project document and task tools for omegon");
Expand All @@ -81,9 +100,15 @@ async fn main() -> Result<()> {
println!(" flynt-agent --help Show this help");
println!();
println!("ENVIRONMENT:");
println!(" FLYNT_PROJECT Project directory (default: cwd if it looks like a project, otherwise ~/Documents/Flynt)");
println!(" Also accepts OMEGON_PROJECT_ROOT from the native extension host.");
println!(" Legacy aliases (deprecated): FLYNT_VAULT, CODEX_VAULT");
println!(
" FLYNT_PROJECT Project directory (default: cwd if it looks like a project, otherwise ~/Documents/Flynt)"
);
println!(
" Also accepts OMEGON_PROJECT_ROOT from the native extension host."
);
println!(
" Legacy aliases (deprecated): FLYNT_VAULT, CODEX_VAULT"
);
}
Some("--rpc") | _ => {
// Default: run as omegon extension (v2 bidirectional protocol)
Expand All @@ -98,7 +123,7 @@ async fn main() -> Result<()> {

#[cfg(test)]
mod tests {
use super::looks_like_project_root;
use super::{is_transport_disconnect_message, looks_like_project_root};

#[test]
fn recognizes_flynt_project_root() {
Expand All @@ -119,4 +144,15 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
assert!(!looks_like_project_root(tmp.path()));
}

#[test]
fn classifies_transport_disconnects() {
assert!(is_transport_disconnect_message("Broken pipe (os error 32)"));
assert!(is_transport_disconnect_message("connection closed"));
assert!(is_transport_disconnect_message(
"transport disconnected while writing response"
));
assert!(!is_transport_disconnect_message("method not found"));
assert!(!is_transport_disconnect_message("invalid params"));
}
}
6 changes: 6 additions & 0 deletions crates/flynt-app/src/components/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ fn TreeFile(meta: DocumentMeta, depth: u32) -> Element {
},
style: "padding-left: {indent + 20.0}px;",
onclick: move |_| {
if let Ok(Some(doc)) = ctx.project().store.get_document(&id) {
let _ = document::eval(&crate::views::notes::cm6_fast_swap_js(&doc.content));
}
tab_state.write().open(id.clone(), title.clone());
// Only write route if we're not already on Notes — avoids
// triggering a full app route re-evaluation for no reason.
Expand Down Expand Up @@ -373,6 +376,9 @@ fn TreeFile(meta: DocumentMeta, depth: u32) -> Element {
*ctx_menu.write() = None;
match action.as_str() {
"open-tab" => {
if let Ok(Some(doc)) = ctx.project().store.get_document(&id_for_tab) {
let _ = document::eval(&crate::views::notes::cm6_fast_swap_js(&doc.content));
}
tab_state.write().open(id_for_tab.clone(), title_for_tab.clone());
*active_route.write() = Route::Notes;
}
Expand Down
22 changes: 17 additions & 5 deletions crates/flynt-app/src/components/tab_bar.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use dioxus::prelude::*;
use flynt_core::store::ProjectStore;
use crate::bootstrap::AppContext;
use crate::state::TabState;
use dioxus::prelude::*;
use flynt_core::store::ProjectStore;

#[component]
pub fn TabBar() -> Element {
let tab_state = use_context::<Signal<TabState>>();
let tabs = tab_state.read().tabs.clone();

if tabs.is_empty() { return rsx! { div { class: "tab-bar tab-bar-empty" } }; }
if tabs.is_empty() {
return rsx! { div { class: "tab-bar tab-bar-empty" } };
}

rsx! {
div { class: "tab-bar",
Expand All @@ -33,7 +35,12 @@
}

#[component]
fn TabItem(index: usize, title: String, doc_id: flynt_core::models::DocumentId, is_active: bool) -> Element {
fn TabItem(

Check warning

Code scanning / lipstyk

string-params Warning

fn TabItem takes owned String — would &str work here?
index: usize,
title: String,
doc_id: flynt_core::models::DocumentId,
is_active: bool,
) -> Element {
let ctx = use_context::<AppContext>();
let mut tab_state = use_context::<Signal<TabState>>();
let mut renaming = use_signal(|| false);
Expand All @@ -44,7 +51,12 @@
rsx! {
div {
class: if is_active { "tab active" } else { "tab" },
onclick: move |_| tab_state.write().active = i,
onclick: move |_| {
if let Ok(Some(doc)) = ctx.project().store.get_document(&doc_id) {
let _ = document::eval(&crate::views::notes::cm6_fast_swap_js(&doc.content));
}
tab_state.write().active = i;
},
ondoubleclick: move |_| {
*rename_input.write() = title.clone();
*renaming.write() = true;
Expand Down
17 changes: 17 additions & 0 deletions crates/flynt-app/src/views/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,23 @@ fn preprocess(src: &str) -> String {

// ── CM6 init JS ─────────────────────────────────────────────────────────────

pub(crate) fn cm6_fast_swap_js(content: &str) -> String {
let escaped = serde_json::to_string(content).unwrap_or_else(|_| "\"\"".into());
format!(
r#"
(function() {{
const container = document.getElementById('flynt-cm-editor');
const cm = window._flyntCM;
if (!container || !cm || !cm.dom || !container.contains(cm.dom)) return false;
const next = {escaped};
cm.dispatch({{ changes: {{ from: 0, to: cm.state.doc.length, insert: next }} }});
cm.scrollDOM.scrollTop = 0;
return true;
}})();
"#
)
}

fn cm6_init_js(content: &str) -> String {
let escaped = serde_json::to_string(content).unwrap_or_else(|_| "\"\"".into());
format!(
Expand Down
Loading