Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
9ab88ba
Updated translations from Lokalise on Sun Aug 24 12:32:21 PDT 2025
marionbarker Aug 25, 2025
d6bd8e7
revert Intents and InfoPlist modifications
marionbarker Aug 28, 2025
ed8e617
Merge pull request #2337 from LoopKit/translations
marionbarker Aug 30, 2025
c647d63
Updated translations from Lokalise on Sat Aug 30 10:22:12 PDT 2025
marionbarker Aug 30, 2025
a634e2f
Updates for iOS26. Fix bolus action button
ps2 Aug 31, 2025
9716cdf
Cleanup
ps2 Aug 31, 2025
c2e1a73
Merge pull request #2340 from LoopKit/translations
marionbarker Aug 31, 2025
01412d9
Update manual dose entry UI for iOS 26
ps2 Sep 1, 2025
eb89c63
Merge pull request #2341 from LoopKit/ps2/ios26
marionbarker Sep 1, 2025
cfff59a
Convert to using xcstrings
marionbarker Sep 5, 2025
9a2dc9d
modify to enable Xcode to export localization
marionbarker Sep 5, 2025
db83e7f
Updated translations from lokalise on Sat Sep 6 12:10:52 PDT 2025
marionbarker Sep 6, 2025
c3c15e1
Update Localization,
marionbarker Sep 7, 2025
2475da2
Updated translations from lokalise on Mon Sep 8 15:14:33 PDT 2025
marionbarker Sep 8, 2025
3784697
Merge pull request #2342 from LoopKit/convert_to_xcstrings
marionbarker Sep 10, 2025
480e89b
Disable Liquid Glass
MikePlante1 Sep 11, 2025
d8ea353
Merge pull request #2343 from MikePlante1/disable-liquid-glass
marionbarker Sep 13, 2025
b9ac026
Support Widget Tinting (#2344)
Camji55 Sep 15, 2025
6fa92d3
Clear bolus recommendation on initial edit
ps2 Sep 19, 2025
8f14e60
Merge pull request #2349 from LoopKit/ps2/clear-recommendation
marionbarker Sep 19, 2025
d28434d
support audio for pump managers that use silent audio for keep-alive
marionbarker Sep 21, 2025
b81d866
Merge pull request #2355 from LoopKit/support_audio
marionbarker Sep 23, 2025
eff86a5
Updated translations from lokalise on Tue Sep 23 15:51:19 PDT 2025
marionbarker Sep 23, 2025
3195335
Merge pull request #2359 from LoopKit/translations
marionbarker Sep 24, 2025
8817c9d
add ce, hu, uk languages
marionbarker Oct 3, 2025
21c8e64
add fake translated words to new languages in Loop
marionbarker Oct 3, 2025
da1231a
Updated translations from lokalise on Fri Oct 3 19:52:42 PDT 2025
marionbarker Oct 4, 2025
d1fd929
Fix Siri string issue for broswer build
marionbarker Oct 7, 2025
29ccb18
Merge branch 'dev' into fix_merge_conflicts
marionbarker Oct 7, 2025
2627fe7
Updated translations from lokalise on Sat Oct 11 09:28:52 PDT 2025
marionbarker Oct 11, 2025
00a29f9
Merge pull request #2364 from LoopKit/lokalise_after_iaps_crowdin
marionbarker Oct 11, 2025
cb0133c
ios26 fixes
ps2 Oct 20, 2025
7c94007
Merge pull request #2371 from LoopKit/ps2/ios26-fixes
marionbarker Oct 21, 2025
4a5e0f4
Updated translations from lokalise on Fri Oct 24 11:10:09 PDT 2025
marionbarker Oct 24, 2025
c54fb10
Merge pull request #2372 from LoopKit/translations
marionbarker Oct 24, 2025
f997819
Updated translations from lokalise on Wed Nov 19 09:07:32 PST 2025
marionbarker Nov 19, 2025
7c05abf
Merge pull request #2378 from LoopKit/translations
marionbarker Nov 21, 2025
7bf62f5
feature: Live Activity (#2191)
bastiaanv Nov 22, 2025
a745f0b
update linting for Live Activity
marionbarker Dec 3, 2025
e924058
Merge pull request #2381 from LoopKit/update_linting
marionbarker Dec 3, 2025
48fefe2
Updated translations from lokalise on Sat Dec 27 14:50:21 PST 2025
marionbarker Dec 27, 2025
db9cf70
Merge pull request #2389 from LoopKit/translations
marionbarker Dec 30, 2025
ec3248d
Updated translations from lokalise on Sun Feb 1 09:46:29 PST 2026
marionbarker Feb 1, 2026
5ea835d
Merge pull request #2398 from LoopKit/translations
marionbarker Feb 1, 2026
6fc8e26
Modify live activity (#2397)
marionbarker Feb 3, 2026
b6e8841
Include submodule SHA in LoopWorkspace build details (#2399)
marionbarker Feb 4, 2026
217a0b4
Add FoodFinder feature: AI-powered food identification for carb entry
taylorpatterson-T1D Feb 8, 2026
128c997
Add generative voice search: route mic input through AI analysis
taylorpatterson-T1D Feb 8, 2026
200a870
Detect keyboard dictation for generative AI search instead of mic button
taylorpatterson-T1D Feb 8, 2026
a338c8e
Fix pbxproj group hierarchy: attach orphaned FoodFinder groups
taylorpatterson-T1D Feb 8, 2026
5786e87
FoodFinder: fix barcode thumbnail loading, add secure API key storage…
taylorpatterson-T1D Feb 9, 2026
a9da314
FoodFinder: performance optimizations and debug print cleanup
taylorpatterson-T1D Feb 9, 2026
5b22e27
FoodFinder: consolidate files, fix long-title overflow, standardize h…
taylorpatterson-T1D Feb 9, 2026
f21361a
FoodFinder: add LoopInsights data hooks and 30-day retention option
taylorpatterson-T1D Feb 9, 2026
bb7dd68
FoodFinder: add data hooks and 30-day retention option
taylorpatterson-T1D Feb 9, 2026
95acc64
Merge remote-tracking branch 'myfork/feat/FoodFinder' into feat/FoodF…
taylorpatterson-T1D Feb 10, 2026
ccc683c
FoodFinder: realistic absorption times, fix OCR routing, inline Why hrs
taylorpatterson-T1D Feb 10, 2026
96393b2
FoodFinder: move Why hrs pill to right of absorption time value
taylorpatterson-T1D Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions Common/Models/BuildDetails.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,20 @@ class BuildDetails {
var workspaceGitBranch: String? {
return dict["com-loopkit-LoopWorkspace-git-branch"] as? String
}

/// Returns a dictionary of submodule details.
/// The keys are the submodule names, and the values are tuples (branch, commitSHA).
var submodules: [String: (branch: String, commitSHA: String)] {
guard let subs = dict["com-loopkit-Loop-submodules"] as? [String: [String: Any]] else {
return [:]
}
var result = [String: (branch: String, commitSHA: String)]()
for (name, info) in subs {
let branch = info["branch"] as? String ?? String(localized: "Unknown")
let commitSHA = info["commit_sha"] as? String ?? String(localized: "Unknown")
result[name] = (branch: branch, commitSHA: commitSHA)
}
return result
}
}

36 changes: 36 additions & 0 deletions Common/ce.lproj/Intents.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* (No Comment) */
"9KhaIS" = "I've set the preset";

/* (No Comment) */
"80eo5o" = "Add Carb Entry";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";

/* (No Comment) */
"I4OZy8" = "Enable Override Preset";

/* (No Comment) */
"lYMuWV" = "Override Name";

/* (No Comment) */
"nDKAmn" = "What's the name of the override you'd like to set?";

/* (No Comment) */
"OcNxIj" = "Add Carb Entry";

/* (No Comment) */
"oLQSsJ" = "Enable '${overrideName}' Override Preset";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";

/* (No Comment) */
"yBzwCL" = "Override Selection";

/* (No Comment) */
"yc02Yq" = "Add a carb entry to Loop";

/* (No Comment) */
"ZZ3mtM" = "Enable an override preset in Loop";

4 changes: 2 additions & 2 deletions Common/fr.lproj/Intents.strings
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
"b085BW" = "Je n'ai pas pu définir le préréglage.";

/* (No Comment) */
"I4OZy8" = "Activer l'ajustement préréglé";
"I4OZy8" = "Activer la surcharge temporaire";

/* (No Comment) */
"lYMuWV" = "Nom Ajustement";

/* (No Comment) */
"nDKAmn" = "Quel est le nom de l'ajustement que vous voulez définir ?";
"nDKAmn" = "Quel est le nom de l'ajustement que vous voulez définir?";

/* (No Comment) */
"OcNxIj" = "Ajouter des glucides";
Expand Down
30 changes: 21 additions & 9 deletions Common/hi.lproj/Intents.strings
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
"80eo5o" = "Add Carb Entry";

/* (No Comment) */
"9KhaIS" = "I've set the preset";

"I4OZy8" = "Enable Override Preset";

"OcNxIj" = "Add Carb Entry";

"XNNmtH" = "Enable preset in Loop";

"ZZ3mtM" = "Enable an override preset in Loop";
/* (No Comment) */
"80eo5o" = "Add Carb Entry";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";

/* (No Comment) */
"I4OZy8" = "Enable Override Preset";

/* (No Comment) */
"lYMuWV" = "Override Name";

/* (No Comment) */
"nDKAmn" = "What's the name of the override you'd like to set?";

/* (No Comment) */
"OcNxIj" = "Add Carb Entry";

/* (No Comment) */
"oLQSsJ" = "Enable '${overrideName}' Override Preset";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";

/* (No Comment) */
"yBzwCL" = "Override Selection";

/* (No Comment) */
"yc02Yq" = "Add a carb entry to Loop";

/* (No Comment) */
"ZZ3mtM" = "Enable an override preset in Loop";

36 changes: 36 additions & 0 deletions Common/hu.lproj/Intents.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* (No Comment) */
"9KhaIS" = "I've set the preset";

/* (No Comment) */
"80eo5o" = "Add Carb Entry";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";

/* (No Comment) */
"I4OZy8" = "Enable Override Preset";

/* (No Comment) */
"lYMuWV" = "Override Name";

/* (No Comment) */
"nDKAmn" = "What's the name of the override you'd like to set?";

/* (No Comment) */
"OcNxIj" = "Add Carb Entry";

/* (No Comment) */
"oLQSsJ" = "Enable '${overrideName}' Override Preset";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";

/* (No Comment) */
"yBzwCL" = "Override Selection";

/* (No Comment) */
"yc02Yq" = "Add a carb entry to Loop";

/* (No Comment) */
"ZZ3mtM" = "Enable an override preset in Loop";

24 changes: 12 additions & 12 deletions Common/it.lproj/Intents.strings
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
/* (No Comment) */
"9KhaIS" = "Ho impostato la preimpostazione";
"9KhaIS" = "Ho impostato il Preset";

/* (No Comment) */
"80eo5o" = "Agg. Carb. Assunti";
"80eo5o" = "Aggiungi inserimento carboidrati";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";
"b085BW" = "Non sono riuscito a impostare il Preset.";

/* (No Comment) */
"I4OZy8" = "Abilita Programma Alternativo";
"I4OZy8" = "Attiva Preset Override";

/* (No Comment) */
"lYMuWV" = "Nome Programma Alternativo";
"lYMuWV" = "Nome Override";

/* (No Comment) */
"nDKAmn" = "Qual'è il nome del programma alternativo che vuoi impostare?";
"nDKAmn" = "Qual'è il nome dell'Override che vuoi impostare?";

/* (No Comment) */
"OcNxIj" = "Agg. Carb. Assunti";
"OcNxIj" = "Aggiungi inserimento carboidrati";

/* (No Comment) */
"oLQSsJ" = "Abilita '${overrideName}' Programma Alternativo";
"oLQSsJ" = "Attiva '${OverrideName}' Preset Override";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";
"XNNmtH" = "Attiva Preset in Loop";

/* (No Comment) */
"yBzwCL" = "Selezione Programma Alternativo";
"yBzwCL" = "Selezione Override";

/* (No Comment) */
"yc02Yq" = "Aggiungi carboidrati a Loop";
"yc02Yq" = "Aggiungi inserimento carboidrati a Loop";

/* (No Comment) */
"ZZ3mtM" = "Attiva Programma Alternativo in Loop";
"ZZ3mtM" = "Attiva un Preset Override in Loop";

14 changes: 7 additions & 7 deletions Common/ro.lproj/Intents.strings
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
"80eo5o" = "Adaugă carbohidrați";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";
"b085BW" = "Nu am reușit să setez presetarea.";

/* (No Comment) */
"I4OZy8" = "Enable Override Preset";
"I4OZy8" = "Activare modificarea personalizată presetată";

/* (No Comment) */
"lYMuWV" = "Override Name";
"lYMuWV" = "Denumirea modificării personalizate";

/* (No Comment) */
"nDKAmn" = "What's the name of the override you'd like to set?";
"nDKAmn" = "Cum se numește modificarea pe care doriți să o setați?";

/* (No Comment) */
"OcNxIj" = "Adaugă carbohidrați";
Expand All @@ -23,14 +23,14 @@
"oLQSsJ" = "Enable '${overrideName}' Override Preset";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";
"XNNmtH" = "Activați presetarea în buclă";

/* (No Comment) */
"yBzwCL" = "Override Selection";
"yBzwCL" = "Selecție modificare personalizată";

/* (No Comment) */
"yc02Yq" = "Adaugă carbohidrați în Loop";

/* (No Comment) */
"ZZ3mtM" = "Enable an override preset in Loop";
"ZZ3mtM" = "Activați o modificare personalizată presetată în Buclă";

36 changes: 36 additions & 0 deletions Common/uk.lproj/Intents.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* (No Comment) */
"9KhaIS" = "I've set the preset";

/* (No Comment) */
"80eo5o" = "Add Carb Entry";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";

/* (No Comment) */
"I4OZy8" = "Enable Override Preset";

/* (No Comment) */
"lYMuWV" = "Override Name";

/* (No Comment) */
"nDKAmn" = "What's the name of the override you'd like to set?";

/* (No Comment) */
"OcNxIj" = "Add Carb Entry";

/* (No Comment) */
"oLQSsJ" = "Enable '${overrideName}' Override Preset";

/* (No Comment) */
"XNNmtH" = "Enable preset in Loop";

/* (No Comment) */
"yBzwCL" = "Override Selection";

/* (No Comment) */
"yc02Yq" = "Add a carb entry to Loop";

/* (No Comment) */
"ZZ3mtM" = "Enable an override preset in Loop";

4 changes: 2 additions & 2 deletions Common/zh-Hans.lproj/Intents.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"9KhaIS" = "I've set the preset";

/* (No Comment) */
"80eo5o" = "添加碳水化合物";
"80eo5o" = "添加碳水摄入";

/* (No Comment) */
"b085BW" = "I wasn't able to set the preset.";
Expand All @@ -17,7 +17,7 @@
"nDKAmn" = "What's the name of the override you'd like to set?";

/* (No Comment) */
"OcNxIj" = "添加碳水化合物";
"OcNxIj" = "添加碳水摄入";

/* (No Comment) */
"oLQSsJ" = "Enable '${overrideName}' Override Preset";
Expand Down
Binary file removed Documentation/Assigning a bundle identifier.png
Binary file not shown.
Binary file removed Documentation/Changing the app icon.png
Binary file not shown.
Binary file removed Documentation/Changing the display name.png
Binary file not shown.
99 changes: 99 additions & 0 deletions Documentation/FoodFinder/FoodFinder_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# FoodFinder for Loop

FoodFinder adds AI-powered food identification and nutrition lookup to Loop's carb entry workflow. It supports barcode scanning (via OpenFoodFacts), AI camera analysis, voice search, and text-based food search — all integrated with a minimal footprint into Loop's existing codebase.

## Features

- **Barcode Scanner** — Scan product barcodes to look up nutrition data from OpenFoodFacts
- **AI Camera Analysis** — Take a photo of food and get AI-powered carb estimates (supports Claude, OpenAI, Google Gemini, and custom BYO providers)
- **Voice Search** — Speak a food name to search for nutrition information
- **Text Search** — Type a food name for quick lookup
- **Favorite Food Thumbnails** — Saved favorites display thumbnail images for easy identification
- **Configurable AI Providers** — Choose between multiple AI backends or bring your own API endpoint

## Architecture

FoodFinder follows the **minimal footprint principle**: all feature logic lives in dedicated `FoodFinder/` subdirectories, with fewer than 30 lines added to existing Loop files.

### Directory Structure

```
Loop/Loop/
├── Views/FoodFinder/ (11 files — all UI components)
├── Models/FoodFinder/ (3 files — data models)
├── Services/FoodFinder/ (13 files — API clients, scanning, AI)
├── View Models/FoodFinder/ (2 files — state management)
├── Resources/FoodFinder/ (1 file — feature flags + settings keys)
└── Documentation/FoodFinder/ (this file)

Loop/LoopTests/FoodFinder/ (3 files — unit tests)
```

### Integration Touchpoints

Only 3 existing Loop files are modified, totaling ~29 lines:

| File | Lines Added | Purpose |
|------|-------------|---------|
| `CarbEntryView.swift` | ~9 | Inserts `FoodFinder_EntryPoint` view |
| `SettingsView.swift` | ~16 | Adds FoodFinder Settings navigation link |
| `FavoriteFoodDetailView.swift` | ~4 | Adds thumbnail display for favorites |

### Key Files

| File | Role |
|------|------|
| `FoodFinder_FeatureFlags.swift` | Central on/off toggle and all UserDefaults keys |
| `FoodFinder_EntryPoint.swift` | Self-contained carb entry UI (search, scan, results) |
| `FoodFinder_SearchViewModel.swift` | All search/scan/AI state management |
| `FoodFinder_SettingsView.swift` | AI provider configuration screen |

## Enabling/Disabling

FoodFinder is controlled by a single toggle in `FoodFinder_FeatureFlags.swift`:

```swift
FoodFinder_FeatureFlags.isEnabled // returns Bool
```

When disabled, all FoodFinder UI is hidden and no FoodFinder code executes. The feature can be toggled via the `foodSearchEnabled` UserDefaults key.

## AI Provider Configuration

FoodFinder supports multiple AI providers for food photo analysis:

1. **Claude** (Anthropic) — Requires API key
2. **OpenAI** (GPT-4 Vision) — Requires API key
3. **Google Gemini** — Requires API key
4. **BYO (Bring Your Own)** — Custom endpoint URL + API key

Providers are configured in Settings > FoodFinder Settings. API keys are stored in UserDefaults with `foodFinder_` prefixed keys.

## Portability

FoodFinder is designed for easy adoption into other Loop forks (Trio, IAPS, Tidepool Loop):

- **No LoopKit submodule changes** — All code lives under the Loop/ submodule
- **Self-contained feature flag** — Single file controls enable/disable
- **Prefixed naming** — All files use `FoodFinder_` prefix to avoid naming conflicts
- **Minimal touchpoints** — Only 3 files need small modifications in the host app
- **Script-installable** — The `FoodFinder/` directories can be copied and the 3 touchpoints applied programmatically

## Dependencies

FoodFinder uses only Apple frameworks available on iOS:

- `Vision` — Barcode detection
- `AVFoundation` — Camera access for scanning and AI analysis
- `Speech` — Voice search recognition
- `SwiftUI` / `UIKit` — User interface

No third-party dependencies are required.

## Testing

Unit tests are located in `LoopTests/FoodFinder/`:

- `FoodFinder_OpenFoodFactsTests.swift` — API response parsing tests
- `FoodFinder_BarcodeScannerTests.swift` — Barcode detection tests
- `FoodFinder_VoiceSearchTests.swift` — Voice recognition tests
Binary file removed Documentation/Screenshots/Phone Bolus.png
Binary file not shown.
Binary file removed Documentation/Screenshots/Phone Graphs.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed Documentation/Screenshots/Watch Bolus.png
Binary file not shown.
Binary file removed Documentation/Screenshots/Watch Carb Entry.png
Binary file not shown.
Binary file removed Documentation/Screenshots/Watch Complication.png
Binary file not shown.
Binary file removed Documentation/Screenshots/Watch Menu.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed Documentation/Testing/Images/mock_managers.png
Binary file not shown.
Binary file removed Documentation/Testing/Images/rewind.png
Binary file not shown.
Binary file removed Documentation/Testing/Images/scenarios_menu.png
Binary file not shown.
Binary file removed Documentation/Testing/Images/scenarios_url.png
Binary file not shown.
Loading