Skip to content

Commit 476d157

Browse files
spacetime dev - Replace template selection with fuzzy-filterable menu (#4470)
This pull request updates the template system to support richer metadata and improves the interactive CLI experience for selecting templates. The main changes are the addition of a `client_framework` field to template metadata, a refactor of the templates JSON generation and parsing, and a redesign of the interactive template selection flow to group templates by language/framework and use fuzzy search for easier navigation. <img width="407" height="409" alt="image" src="https://github.com/user-attachments/assets/d3548505-80e8-4778-8bfb-71d5e3fe31e9" /> **Template Metadata and Serialization Improvements:** * Added a new `client_framework` field to all template metadata files (e.g., `.template.json`) and updated the Rust structs (`TemplateInfo`, `TemplateDefinition`, etc.) to support this field, enabling more descriptive and flexible template selection. [[1]](diffhunk://#diff-5438edbe7b41e3e0a1a62ce0ebde2a833a0441d76c6ffdf16093a3cff258f462R3) [[2]](diffhunk://#diff-34a44755a07373e9c2872db87a64c3fd752cfee7d8d536045909daf861ea4728R3) [[3]](diffhunk://#diff-a826c5a2eac977cff44022d72856ec4b1af176ffbb50697f22857e1e3d478aa1R3) [[4]](diffhunk://#diff-7a21474fbc5bbbd989e89f32aa8dcc25fcc6fe4ac43d418c42b2b38603d21714R3) [[5]](diffhunk://#diff-d8ee9fd0ef15ed23f0f7f38e556d789bdefc6c37dab67fbfccebc513b9da871cR3) [[6]](diffhunk://#diff-7224fd4300f9c5af7834ff5989a76a399ce9117c28f5d7e2c1fbd6bdaf45be1bR3) [[7]](diffhunk://#diff-8b015b0ce6339c444ef5ef4dc5e862275d98967e5c189da37b51bdaa58443606R3) [[8]](diffhunk://#diff-89e32f7fc9fc42863998d7651bc7fa1fdfb352e8ac3ef45792e3f1374052c9ddR3) [[9]](diffhunk://#diff-8259d300fda149c968a68989aec2bc2afa479aded649ac6b8d3c4b277f610542R3) [[10]](diffhunk://#diff-bb8b9202e51fb7038efa2f2bb23872afa02caf71ab3a0fb7f513ca7822c1b8faR3) [[11]](diffhunk://#diff-18497b72a2306fc2560475e2548cfe1b96b6206c395380437ba1fafefd66e126L199-R221) [[12]](diffhunk://#diff-18497b72a2306fc2560475e2548cfe1b96b6206c395380437ba1fafefd66e126R279) * Refactored the templates JSON generation to use `serde` for serialization, replacing manual string building, and ensured that optional string fields serialize as empty strings when not present. **CLI Interactive Template Selection Redesign:** * Removed the previous "highlights" concept and reworked the interactive selection to group templates by language/framework combinations, showing counts and using fuzzy search for easier filtering. [[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL38-L48) [[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL772-R833) [[3]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL813-L850) * Updated the selection menus to allow users to pick a language/framework group, then choose from multiple templates if available, or opt to clone from GitHub or select "None." [[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL772-R833) [[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL813-L850) * Improved language label formatting for better user experience in the CLI prompt. **Codebase Cleanup and API Changes:** * Removed unused highlight-related structs and logic from the CLI, simplifying the template fetching API to return only templates. [[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL38-L48) [[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL198-R198) * Updated all template selection logic to use the new API and data structures. [[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL683-R679) [[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL748-R744) These changes make template selection more intuitive and scalable as more templates and frameworks are added. --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
1 parent 5e2dda9 commit 476d157

12 files changed

Lines changed: 165 additions & 160 deletions

File tree

crates/cli/build.rs

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,29 @@ fn generate_template_files() {
196196
write_if_changed(&dest_path, generated_code.as_bytes()).expect("Failed to write embedded_templates.rs");
197197
}
198198

199-
#[derive(Debug)]
199+
#[derive(Debug, serde::Serialize)]
200200
struct TemplateInfo {
201201
id: String,
202202
description: String,
203+
#[serde(serialize_with = "serialize_option_string_as_empty")]
203204
server_source: Option<String>,
205+
#[serde(serialize_with = "serialize_option_string_as_empty")]
204206
client_source: Option<String>,
205207
server_lang: Option<String>,
206208
client_lang: Option<String>,
209+
client_framework: Option<String>,
210+
}
211+
212+
#[derive(serde::Serialize)]
213+
struct TemplatesJson<'a> {
214+
templates: &'a [TemplateInfo],
207215
}
208216

209217
#[derive(serde::Deserialize)]
210218
struct TemplateMetadata {
211219
description: String,
212220
client_lang: Option<String>,
221+
client_framework: Option<String>,
213222
server_lang: Option<String>,
214223
}
215224

@@ -267,6 +276,7 @@ fn discover_templates(templates_dir: &Path) -> Vec<TemplateInfo> {
267276
client_source,
268277
server_lang: metadata.server_lang,
269278
client_lang: metadata.client_lang,
279+
client_framework: metadata.client_framework,
270280
});
271281
}
272282
}
@@ -276,57 +286,15 @@ fn discover_templates(templates_dir: &Path) -> Vec<TemplateInfo> {
276286
}
277287

278288
fn generate_templates_json(templates: &[TemplateInfo]) -> String {
279-
let mut json = String::from("{\n \"highlights\": [\n");
280-
281-
for template in templates {
282-
if template.id.contains("react") {
283-
json.push_str(" { \"name\": \"React\", \"template_id\": \"");
284-
json.push_str(&template.id);
285-
json.push_str("\" }\n");
286-
break;
287-
}
288-
}
289-
290-
json.push_str(" ],\n \"templates\": [\n");
291-
292-
for (i, template) in templates.iter().enumerate() {
293-
json.push_str(" {\n");
294-
json.push_str(&format!(" \"id\": \"{}\",\n", template.id));
295-
json.push_str(&format!(" \"description\": \"{}\",\n", template.description));
296-
297-
if let Some(ref server_source) = template.server_source {
298-
json.push_str(&format!(" \"server_source\": \"{}\",\n", server_source));
299-
} else {
300-
json.push_str(" \"server_source\": \"\",\n");
301-
}
302-
303-
if let Some(ref client_source) = template.client_source {
304-
json.push_str(&format!(" \"client_source\": \"{}\",\n", client_source));
305-
} else {
306-
json.push_str(" \"client_source\": \"\",\n");
307-
}
308-
309-
if let Some(ref server_lang) = template.server_lang {
310-
json.push_str(&format!(" \"server_lang\": \"{}\",\n", server_lang));
311-
} else {
312-
json.push_str(" \"server_lang\": \"\",\n");
313-
}
314-
315-
if let Some(ref client_lang) = template.client_lang {
316-
json.push_str(&format!(" \"client_lang\": \"{}\"", client_lang));
317-
} else {
318-
json.push_str(" \"client_lang\": \"\"");
319-
}
320-
321-
json.push_str("\n }");
322-
if i < templates.len() - 1 {
323-
json.push(',');
324-
}
325-
json.push('\n');
326-
}
289+
let payload = TemplatesJson { templates };
290+
serde_json::to_string_pretty(&payload).expect("Failed to serialize templates JSON")
291+
}
327292

328-
json.push_str(" ]\n}");
329-
json
293+
fn serialize_option_string_as_empty<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
294+
where
295+
S: serde::Serializer,
296+
{
297+
serializer.serialize_str(value.as_deref().unwrap_or(""))
330298
}
331299

332300
fn generate_template_entry(code: &mut String, template_path: &Path, source: &str, manifest_dir: &Path) {

0 commit comments

Comments
 (0)