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
320 changes: 320 additions & 0 deletions FUTURE_ROADMAPS.md

Large diffs are not rendered by default.

64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,68 @@ Bundled templates include Standard Meeting, Daily Standup, Project Sync, Retrosp

The summary workflow does not send the recorded audio file to an external AI provider. Provider retention and account policies still apply to text sent to that provider. See [Privacy and data handling](PRIVACY_POLICY.md).

#### Custom summary templates

A template controls how a meeting record is structured: each section's `instruction` tells the AI what to write, and the section order is the output order. Manage templates in **Settings > Summary > Summary Templates**.

- **New template** builds one from scratch; **Duplicate** starts from an existing one.
- **Edit** a built-in template to save a **Custom** copy with the same name. Deleting the custom copy restores the original built-in.
- Toggle **Form / JSON** to edit the raw template JSON. **Validate** checks it before saving.
- **Import JSON** pastes a template someone shared; **Copy JSON** exports one to your clipboard.

Template JSON has this shape:

```json
{
"name": "Client Call",
"description": "Summary tuned for external client calls",
"sections": [
{
"title": "Overview",
"instruction": "Summarize the purpose and outcome of the call",
"format": "paragraph"
},
{
"title": "Decisions",
"instruction": "List the decisions that were agreed",
"format": "list"
},
{
"title": "Action Items",
"instruction": "List each commitment and who owns it",
"format": "list",
"item_format": "- [owner]: [task] (due [date])"
}
]
}
```

A minimal template needs only a name, a description, and one section:

```json
{
"name": "One-liner",
"description": "A single-paragraph recap",
"sections": [
{ "title": "Summary", "instruction": "Summarize the meeting in one short paragraph", "format": "paragraph" }
]
}
```

Field reference:

| Field | Required | Notes |
|---|---|---|
| `name` | yes | Display name (max 120 chars). |
| `description` | yes | When to use it (max 500 chars). |
| `sections` | yes | 1–30 sections; titles must be unique; order is the output order. |
| `sections[].title` | yes | Section heading (max 120 chars). |
| `sections[].instruction` | yes | What the AI extracts or writes (max 2000 chars). |
| `sections[].format` | yes | One of `paragraph`, `list`, or `string`. |
| `sections[].item_format` | no | Optional per-item hint for `list` sections (max 500 chars). |

To get Trusted Memory suggestions, keep sections for decisions, action items/commitments, and open questions (see below). Templates are plain JSON files, so power users can back them up or share them directly: `%APPDATA%\Briefli\templates\` on Windows, `~/Library/Application Support/Briefli/templates/` on macOS, and `~/.config/Briefli/templates/` on Linux. The fixed multi-language and safety instructions are added automatically and are not part of the template.

### Trusted Memory

Memory suggestions are created from recognized sections in a generated meeting record. A custom summary that omits decisions, action items/commitments/next steps, and open questions may produce no Memory suggestions.
Expand Down Expand Up @@ -148,7 +210,7 @@ Supported inputs include MP4, M4A, WAV, MP3, FLAC, OGG, AAC, MKV, WebM, and WMA.
- **General:** recording notifications and the local recordings folder.
- **Recordings:** audio saving, microphone/system devices, and in-person mode.
- **Transcription:** install and select local Whisper or Parakeet models.
- **Summary:** choose local or remote AI, control automatic summaries, and set preferred languages.
- **Summary:** choose local or remote AI, control automatic summaries, set preferred languages, and create or edit summary templates.
- **Beta:** enable audio import and retranscription.
- **About > Check for Updates:** check GitHub Releases and install a signed update.

Expand Down
3 changes: 3 additions & 0 deletions frontend/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,9 @@ pub fn run() {
summary::template_commands::api_list_templates,
summary::template_commands::api_get_template_details,
summary::template_commands::api_validate_template,
summary::template_commands::api_get_template_content,
summary::template_commands::api_save_template,
summary::template_commands::api_delete_template,
// Built-in AI commands
summary::summary_engine::commands::builtin_ai_list_models,
summary::summary_engine::commands::builtin_ai_get_model_info,
Expand Down
80 changes: 76 additions & 4 deletions frontend/src-tauri/src/summary/template_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ pub struct TemplateInfo {

/// Brief description of the template's purpose
pub description: String,

/// Where the effective template resolves from: "custom", "bundled", or "built_in".
pub source: String,

/// Whether the template can be edited (always true — built-ins/bundled are
/// edited by saving a same-id custom override).
pub editable: bool,

/// Whether the template can be deleted (only user "custom" overrides).
pub deletable: bool,
}

/// Detailed template structure for preview/debugging
Expand Down Expand Up @@ -49,10 +59,16 @@ pub async fn api_list_templates<R: Runtime>(

let template_infos: Vec<TemplateInfo> = templates
.into_iter()
.map(|(id, name, description)| TemplateInfo {
id,
name,
description,
.map(|(id, name, description)| {
let source = templates::template_source(&id);
TemplateInfo {
deletable: source == "custom",
editable: true,
source: source.to_string(),
id,
name,
description,
}
})
.collect();

Expand Down Expand Up @@ -126,6 +142,62 @@ pub async fn api_validate_template<R: Runtime>(
}
}

/// Gets the full editable content of a template.
///
/// Unlike [`api_get_template_details`] (which returns only section titles), this
/// returns the complete `Template` (name, description, and every section field)
/// so a template editor can load and modify it.
#[tauri::command]
pub async fn api_get_template_content<R: Runtime>(
_app: tauri::AppHandle<R>,
template_id: String,
) -> Result<templates::Template, String> {
info!("api_get_template_content called for template_id: {}", template_id);
templates::get_template(&template_id)
}

/// Saves (creates or overwrites) a custom template in the user's data directory.
///
/// Saving with the same id as a built-in/bundled template creates an override.
/// The JSON is validated before it is written.
///
/// # Returns
/// The refreshed [`TemplateInfo`] for the saved template.
#[tauri::command]
pub async fn api_save_template<R: Runtime>(
_app: tauri::AppHandle<R>,
template_id: String,
template_json: String,
) -> Result<TemplateInfo, String> {
info!("api_save_template called for template_id: {}", template_id);

templates::save_custom_template(&template_id, &template_json)?;

let template = templates::get_template(&template_id)?;
let source = templates::template_source(&template_id);
Ok(TemplateInfo {
deletable: source == "custom",
editable: true,
source: source.to_string(),
id: template_id,
name: template.name,
description: template.description,
})
}

/// Deletes a custom template override from the user's data directory.
///
/// Only user (custom) templates can be deleted; built-in/bundled definitions are
/// never removed. Deleting an override reverts the id to its original.
#[tauri::command]
pub async fn api_delete_template<R: Runtime>(
_app: tauri::AppHandle<R>,
template_id: String,
) -> Result<(), String> {
info!("api_delete_template called for template_id: {}", template_id);
templates::delete_custom_template(&template_id)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading
Loading