-
-
Notifications
You must be signed in to change notification settings - Fork 1
Web UI: Implement interactive tour explorer and server #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a3c0aa
efffb80
a4228b1
eb89b7e
28146b6
bd27af8
5136680
87134ea
d8e3c88
625b27c
a44e9a6
fea9b6e
3f52e65
66677a0
45c738e
8f5865a
534d328
162b989
ee03b34
02a7dbc
8d34681
b85fd54
16b4314
578af2d
cff45f6
69bccd3
2bedd18
0cb6c5a
0e9c43e
8da9008
846d7e2
59ebd51
03751f9
d7097aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| use codetours_server::highlight::{get_cache, highlight}; | ||
| use criterion::{Criterion, criterion_group, criterion_main}; | ||
| use std::hint::black_box; | ||
|
|
||
| fn bench_highlight(c: &mut Criterion) { | ||
| let language = "rust"; | ||
| let content = r#" | ||
| pub fn fibonacci(n: u32) -> u32 { | ||
| match n { | ||
| 0 => 1, | ||
| 1 => 1, | ||
| _ => fibonacci(n - 1) + fibonacci(n - 2), | ||
| } | ||
| } | ||
| "#; | ||
|
|
||
| // Warm up the cache | ||
| let _ = highlight(language, content); | ||
|
|
||
| c.bench_function("highlight_cached", |b| { | ||
| b.iter(|| highlight(black_box(language), black_box(content))); | ||
| }); | ||
|
|
||
| c.bench_function("highlight_uncached", |b| { | ||
| b.iter(|| { | ||
| // clear the cache each iteration to simulate a miss | ||
| get_cache().clear(); | ||
| highlight(black_box(language), black_box(content)) | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| criterion_group!(benches, bench_highlight); | ||
| criterion_main!(benches); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| use std::env; | ||
| use std::process::Command; | ||
|
|
||
| fn main() { | ||
| println!("cargo:rerun-if-changed=static/app.css"); | ||
| println!("cargo:rerun-if-changed=templates/"); | ||
|
|
||
| let out_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); | ||
| let input = format!("{}/static/app.css", out_dir); | ||
| let output = format!("{}/static/app.generated.css", out_dir); | ||
|
|
||
| let skip_tailwind = env::var("SKIP_TAILWIND_BUILD").unwrap_or_default() == "1"; | ||
| if skip_tailwind { | ||
| println!("cargo:warning=SKIP_TAILWIND_BUILD is set, skipping regeneration."); | ||
| return; | ||
| } | ||
|
|
||
| let status = Command::new("tailwindcss") | ||
| .args(["-i", &input, "-o", &output, "--minify"]) | ||
| .status() | ||
| .or_else(|_| { | ||
| Command::new("../../tailwindcss") | ||
| .args(["-i", &input, "-o", &output, "--minify"]) | ||
| .status() | ||
| }); | ||
|
|
||
| match status { | ||
| Ok(status) if status.success() => (), | ||
| Ok(status) => { | ||
| eprintln!("Fatal: tailwindcss exited with status {status}."); | ||
| std::process::exit(1); | ||
| } | ||
| Err(err) => { | ||
| eprintln!("Error: tailwindcss CLI not found or failed."); | ||
| eprintln!("Details: {err}"); | ||
| eprintln!("Please install it via 'cargo binstall tailwindcss' or 'mise install'."); | ||
| // If the generated file already exists (e.g., committed in CI), allow the | ||
| // build to proceed with the stale artifact rather than blocking CI entirely. | ||
| let output_path = std::path::Path::new(&output); | ||
| if !output_path.exists() { | ||
| eprintln!("Fatal: {} does not exist. Cannot continue.", output); | ||
| std::process::exit(1); | ||
| } else { | ||
| eprintln!("Warning: using existing {} — CSS may be stale.", output); | ||
| } | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| use syntect_assets::assets::HighlightingAssets; | ||
|
|
||
| fn main() { | ||
| let assets = HighlightingAssets::from_binary(); | ||
| let ss = assets.get_syntax_set().unwrap(); | ||
| println!("Total syntaxes: {}", ss.syntaxes().len()); | ||
|
|
||
| // Check for Swift | ||
| let swift_syntax = ss.find_syntax_by_name("Swift"); | ||
| match swift_syntax { | ||
| Some(s) => println!("Found Swift: {} - extensions: {:?}", s.name, s.file_extensions), | ||
| None => println!("Swift NOT found in syntax set"), | ||
| } | ||
|
|
||
| // Check if we can find it by extension | ||
| let by_ext = ss.find_syntax_by_extension("swift"); | ||
| match by_ext { | ||
| Some(s) => println!("Found by extension 'swift': {}", s.name), | ||
| None => println!("NOT found by extension 'swift'"), | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [filter] | ||
| tag_color_classes = "crate::web::filters::tag_color_classes" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| use crate::web::NavItem; | ||
| use crate::{auth::AuthContext, router::AppState}; | ||
| use axum::{ | ||
| extract::{Form, State}, | ||
| http::StatusCode, | ||
| response::IntoResponse, | ||
| }; | ||
| use rinja::Template; | ||
| use serde::Deserialize; | ||
|
|
||
| #[derive(Template)] | ||
| #[template(path = "config/index.html")] | ||
| pub struct ConfigTemplate { | ||
| pub nav: NavItem, | ||
| pub repos: Vec<RepoView>, | ||
| pub prefs: PrefsView, | ||
| } | ||
|
|
||
| pub struct PrefsView { | ||
| pub theme: String, | ||
| pub font: String, | ||
| } | ||
|
|
||
| pub struct RepoView { | ||
| pub name: String, | ||
| pub path: String, | ||
| pub connected: bool, | ||
| } | ||
|
|
||
| #[derive(Deserialize)] | ||
| pub struct PrefsForm { | ||
| pub theme: String, | ||
| pub font: String, | ||
| } | ||
|
|
||
| pub async fn page_handler(State(state): State<AppState>, _auth: AuthContext) -> impl IntoResponse { | ||
| let db = match state.storage.get_conn().await { | ||
| Ok(t) => t, | ||
| Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), | ||
| }; | ||
|
|
||
| let repos = db | ||
| .interact(|conn| { | ||
| let mut stmt = conn | ||
| .prepare( | ||
| " | ||
| SELECT DISTINCT repo_url | ||
| FROM collections | ||
| WHERE repo_url IS NOT NULL AND repo_url != '' | ||
| ", | ||
| ) | ||
| .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||
|
|
||
| let urls = stmt | ||
| .query_map([], |row| row.get::<_, String>(0)) | ||
| .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? | ||
| .collect::<Result<Vec<_>, _>>() | ||
| .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||
|
|
||
| let mut repos = Vec::new(); | ||
| for url in urls { | ||
| let name = url.split('/').next_back().unwrap_or(&url).to_string(); | ||
| repos.push(RepoView { name, path: url, connected: true }); | ||
|
Comment on lines
+62
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle trailing slashes when deriving repository names. If Suggested fix- let name = url.split('/').next_back().unwrap_or(&url).to_string();
+ let normalized = url.trim_end_matches('/');
+ let name = normalized
+ .rsplit('/')
+ .find(|segment| !segment.is_empty())
+ .unwrap_or(normalized)
+ .to_string();🤖 Prompt for AI Agents |
||
| } | ||
| Ok::<_, StatusCode>(repos) | ||
| }) | ||
| .await | ||
| .unwrap_or(Err(StatusCode::INTERNAL_SERVER_ERROR)); | ||
|
|
||
| match repos { | ||
| Ok(repos) => { | ||
| let prefs = | ||
| PrefsView { theme: "atom-one-dark".to_string(), font: "Fira Code".to_string() }; | ||
| ConfigTemplate { nav: NavItem::Config, repos, prefs }.into_response() | ||
| } | ||
| Err(e) => e.into_response(), | ||
| } | ||
| } | ||
|
|
||
| pub async fn prefs_handler( | ||
| State(_state): State<AppState>, | ||
| _auth: AuthContext, | ||
| Form(_form): Form<PrefsForm>, | ||
| ) -> impl IntoResponse { | ||
| // TODO: Persist preferences in Phase 10 | ||
| StatusCode::NOT_IMPLEMENTED.into_response() | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| pub async fn stub_handler() -> impl IntoResponse { | ||
| (StatusCode::NOT_IMPLEMENTED, "This feature will be available in Phase 10 or later.") | ||
| .into_response() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| use axum::response::{IntoResponse, Redirect}; | ||
|
|
||
| pub async fn handler() -> impl IntoResponse { | ||
| Redirect::temporary("/tours") | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.