Skip to content

Commit b050536

Browse files
committed
Remove outdated WeChat assets and documentation for CodexSwitch integration with DeepSeek
1 parent ea91e32 commit b050536

270 files changed

Lines changed: 456 additions & 32247 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ FakesAssemblies/
292292
# Node.js Tools for Visual Studio
293293
.ntvs_analysis.dat
294294
node_modules/
295+
.npm-cache/
295296

296297
# Visual Studio 6 build log
297298
*.plg

‎AGENTS.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
5+
`CodexSwitch/` contains the Avalonia desktop app targeting .NET 10 and using MVVM. Keep UI under `Views/` and `Controls/`, state and commands under `ViewModels/`, and business logic in `Services/`, `Proxy/`, `Models/`, and `I18n/`. Static assets live in `CodexSwitch/Assets/`.
6+
7+
`CodexSwitch.Tests/` holds xUnit coverage for pricing, routing, config writers, usage parsing, i18n, and update flows. Match new tests to the production subject, for example `PriceCalculatorTests.cs`.
8+
9+
`CodexSwitch.AdminWeb/` is the React + Vite admin UI. Source is in `src/`, public assets in `public/`, and production output in `dist/`. `build/` contains release helpers; `docs/` and `docs-site/` hold documentation.
10+
11+
## Build, Test, and Development Commands
12+
13+
- `dotnet restore CodexSwitch.Tests/CodexSwitch.Tests.csproj` — restore .NET dependencies.
14+
- `dotnet run --project CodexSwitch/CodexSwitch.csproj` — start the desktop app locally.
15+
- `dotnet build CodexSwitch.Tests/CodexSwitch.Tests.csproj -c Release --no-restore` — build the app and test project.
16+
- `dotnet test CodexSwitch.Tests/CodexSwitch.Tests.csproj -c Release --no-build --no-restore` — run the full xUnit suite.
17+
- In `CodexSwitch.AdminWeb/`: `npm run dev`, `npm run build`, and `npm run lint` — develop, bundle, and lint the admin UI. Release builds of the desktop app invoke the admin web build when `package.json` is present.
18+
19+
## Coding Style & Naming Conventions
20+
21+
Use 4 spaces in C# and XAML. Follow existing formatting in TS/React files. Public types, properties, and methods use PascalCase; private fields use `_camelCase`. Keep nullable annotations enabled and prefer Avalonia compiled bindings. Comments should explain intent, invariants, or non-obvious constraints—not restate code. Use `eslint` for AdminWeb changes and keep imports clean.
22+
23+
## Testing Guidelines
24+
25+
Write xUnit tests in `CodexSwitch.Tests/` for any behavior change. Name test files after the subject under test and test methods in `Action_ExpectedResult` style, for example `Calculate_AppliesProgressiveTiersAndFastOverride`. Cover routing, protocol conversion, pricing, config writing, usage accounting, and UI-facing service behavior.
26+
27+
## Commit & Pull Request Guidelines
28+
29+
Recent history mixes conventional commits (`feat:`, `fix:`, `chore:`) with a few free-form messages. Prefer conventional prefixes for all new commits. Keep PRs focused, describe the user-visible impact, link related issues, and include screenshots for UI changes. Before opening a PR, run the relevant .NET tests and `npm run lint` for AdminWeb work.
30+
31+
## Security & Configuration Tips
32+
33+
Treat config files and exported settings as sensitive; they may contain API keys or OAuth tokens. The local proxy defaults to `127.0.0.1:12785`. Managed config writes create `.bak` backups—preserve them unless the change explicitly replaces the managed file flow.

‎CodexSwitch.Tests/AppViewMigrationTests.cs‎

Lines changed: 33 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -5,186 +5,64 @@ namespace CodexSwitch.Tests;
55
public sealed class AppViewMigrationTests
66
{
77
[Fact]
8-
public void ActiveViewsUseCodexSwitchUiFormInputs()
8+
public void LegacyAvaloniaAdminUiFilesAreRemoved()
99
{
10-
var viewsRoot = FindRepoDirectory("CodexSwitch", "Views");
11-
var viewFiles = Directory.EnumerateFiles(viewsRoot, "*.axaml", SearchOption.AllDirectories);
10+
var projectRoot = FindRepoDirectory("CodexSwitch");
1211

13-
foreach (var file in viewFiles)
14-
{
15-
var source = File.ReadAllText(file);
16-
17-
Assert.DoesNotContain("<ui:CsInput", source);
18-
Assert.DoesNotContain("<ui:CsSelect", source);
19-
Assert.DoesNotContain("<ui:CsTextarea", source);
20-
Assert.DoesNotContain("<ui:CsSwitch", source);
21-
Assert.DoesNotContain("<ui:CsSegmentedControl", source);
22-
Assert.DoesNotContain("<ui:CsSegmentedButton", source);
23-
Assert.DoesNotContain("<ui:CsSection", source);
24-
}
25-
}
26-
27-
[Fact]
28-
public void ProviderEditorDialogUsesCodexSwitchUiFormInputs()
29-
{
30-
var source = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "ProviderEditorDialog.axaml"));
31-
32-
Assert.DoesNotContain("<ui:CsInput", source);
33-
Assert.DoesNotContain("<ui:CsSelect", source);
34-
Assert.DoesNotContain("<ui:CsTextarea", source);
35-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"", source);
36-
Assert.Contains("<cui:CodexSelect ItemsSource=\"{Binding ProtocolOptions}\"", source);
37-
Assert.Contains("<cui:CodexSwitch Content=\"{i18n:Tr providerDialog.supportsCodex}\"", source);
38-
Assert.Contains("IsReadOnly=\"{Binding IsDefault}\"", source);
39-
Assert.Contains("IsEnabled=\"{Binding CanEditTarget}\"", source);
40-
}
41-
42-
[Fact]
43-
public void SettingsPageUsesCodexSwitchUiFormInputs()
44-
{
45-
var source = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Pages", "SettingsPage.axaml"));
46-
47-
Assert.DoesNotContain("<ui:CsInput", source);
48-
Assert.DoesNotContain("<ui:CsSelect", source);
49-
Assert.DoesNotContain("<ui:CsTextarea", source);
50-
Assert.DoesNotContain("<ui:CsButton", source);
51-
Assert.DoesNotContain("<ui:CsIconButton", source);
52-
Assert.Contains("<cui:CodexSelect ItemsSource=\"{Binding SupportedLanguages}\"", source);
53-
Assert.Contains("<cui:CodexSelect.ItemTemplate>", source);
54-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding ProxyListenHost}\"", source);
55-
Assert.Contains("<cui:CodexSelect ItemsSource=\"{Binding HttpVersionOptions}\"", source);
56-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding InboundApiKey}\"", source);
57-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding DefaultFastMultiplier}\"", source);
58-
Assert.Contains("<cui:CodexSwitch Content=\"{i18n:Tr settings.desktop.startWithWindows}\"", source);
59-
Assert.Contains("<cui:CodexSwitch Content=\"{i18n:Tr settings.version.autoCheck}\"", source);
60-
Assert.Contains("<cui:CodexSegmentedControl Grid.Row=\"1\">", source);
61-
Assert.Contains("<cui:CodexSegmentedButton IsSelected=\"{Binding IsGeneralSettingsVisible}\"", source);
62-
Assert.Contains("<cui:CodexSegmentedButton IsSelected=\"{Binding IsLightThemeSelected}\"", source);
63-
Assert.Contains("<cui:CodexSegmentedButton IsSelected=\"{Binding IsSystemNetworkProxySelected}\"", source);
64-
Assert.Contains("<cui:CodexSection Title=\"{i18n:Tr settings.language.title}\"", source);
65-
Assert.Contains("<cui:CodexSection Title=\"{i18n:Tr settings.version.title}\"", source);
66-
Assert.Contains("<cui:CodexIconButton Variant=\"Outline\"", source);
67-
Assert.Contains("Command=\"{Binding BackFromSettingsCommand}\"", source);
68-
Assert.Contains("Command=\"{Binding ApplyCommand}\"", source);
69-
Assert.Contains("Command=\"{Binding SaveCommand}\"", source);
70-
Assert.Contains("Command=\"{Binding CheckForUpdatesCommand}\"", source);
71-
Assert.Contains("Command=\"{Binding OpenDownloadedUpdateCommand}\"", source);
72-
Assert.Contains("Command=\"{Binding OpenLatestReleaseCommand}\"", source);
73-
}
74-
75-
[Fact]
76-
public void LastFormInputSurfacesUseCodexSwitchUiControls()
77-
{
78-
var importDialog = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "CodexAuthImportDialog.axaml"));
79-
var claudePage = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Pages", "ClaudePage.axaml"));
80-
var addProviderPage = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Pages", "AddProviderPage.axaml"));
81-
82-
Assert.Contains("<cui:CodexTextarea Text=\"{Binding CodexAuthImportJson, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"", importDialog);
83-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding ClaudeCodeModel, Mode=TwoWay}\"", claudePage);
84-
Assert.Contains("<cui:CodexSwitch Content=\"{i18n:Tr claude.think}\"", claudePage);
85-
Assert.Contains("<cui:CodexTextBox Text=\"{Binding DisplayName}\"", addProviderPage);
86-
Assert.Contains("<cui:CodexSwitch Content=\"{i18n:Tr providerDialog.supportsCodex}\"", addProviderPage);
87-
}
88-
89-
[Fact]
90-
public void ClaudePageUsesCodexSwitchUiActionButtons()
91-
{
92-
var source = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Pages", "ClaudePage.axaml"));
93-
94-
Assert.DoesNotContain("<ui:CsButton", source);
95-
Assert.Contains("<cui:CodexButton Grid.Column=\"1\"", source);
96-
Assert.Contains("Command=\"{Binding ShowProvidersCommand}\"", source);
97-
Assert.Contains("Command=\"{Binding SelectCommand}\"", source);
98-
Assert.Contains("Command=\"{Binding EditCommand}\"", source);
99-
Assert.Contains("Command=\"{Binding #Root.DataContext.SelectClaudeCodeModelCommand}\"", source);
100-
Assert.Contains("Command=\"{Binding SaveClaudeCodeSettingsCommand}\"", source);
101-
Assert.Contains("<cui:CodexButton.LeadingIcon>", source);
12+
Assert.False(Directory.Exists(Path.Combine(projectRoot, "Views")));
13+
Assert.False(Directory.Exists(Path.Combine(projectRoot, "Controls")));
14+
Assert.False(Directory.Exists(Path.Combine(projectRoot, "Styles")));
15+
Assert.False(File.Exists(Path.Combine(projectRoot, "ViewLocator.cs")));
10216
}
10317

10418
[Fact]
105-
public void DeleteDialogsUseCodexSwitchUiActionButtons()
19+
public void DesktopProjectDoesNotReferenceLegacyAdminUiPackages()
10620
{
107-
var deleteModelDialog = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "DeleteModelDialog.axaml"));
108-
var deleteProviderDialog = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "DeleteProviderDialog.axaml"));
109-
110-
Assert.DoesNotContain("<ui:CsButton", deleteModelDialog);
111-
Assert.DoesNotContain("<ui:CsButton", deleteProviderDialog);
112-
Assert.Contains("<cui:CodexButton Variant=\"Outline\"", deleteModelDialog);
113-
Assert.Contains("<cui:CodexButton Variant=\"Destructive\"", deleteModelDialog);
114-
Assert.Contains("Command=\"{Binding CancelRemovePricingModelCommand}\"", deleteModelDialog);
115-
Assert.Contains("Command=\"{Binding ConfirmRemovePricingModelCommand}\"", deleteModelDialog);
116-
Assert.Contains("<cui:CodexButton Variant=\"Outline\"", deleteProviderDialog);
117-
Assert.Contains("<cui:CodexButton Variant=\"Destructive\"", deleteProviderDialog);
118-
Assert.Contains("Command=\"{Binding CancelRemoveProviderCommand}\"", deleteProviderDialog);
119-
Assert.Contains("Command=\"{Binding ConfirmRemoveProviderCommand}\"", deleteProviderDialog);
21+
var repoRoot = FindRepoDirectory();
22+
var projectSource = File.ReadAllText(Path.Combine(repoRoot, "CodexSwitch", "CodexSwitch.csproj"));
23+
var packagesSource = File.ReadAllText(Path.Combine(repoRoot, "Directory.Packages.props"));
24+
25+
Assert.DoesNotContain("Avalonia.Controls.WebView", projectSource);
26+
Assert.DoesNotContain("Avalonia.Controls.WebView", packagesSource);
27+
Assert.DoesNotContain("Avalonia.Themes.Fluent", projectSource);
28+
Assert.DoesNotContain("Avalonia.Themes.Fluent", packagesSource);
29+
Assert.DoesNotContain("Lucide.Avalonia", projectSource);
30+
Assert.DoesNotContain("Lucide.Avalonia", packagesSource);
12031
}
12132

12233
[Fact]
123-
public void EditorAndImportDialogsUseCodexSwitchUiActionButtons()
34+
public void AdminWebIsServedAtRootInsteadOfAdminBasePath()
12435
{
125-
var modelEditorDialog = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "ModelEditorDialog.axaml"));
126-
var importDialog = File.ReadAllText(FindRepoFile("CodexSwitch", "Views", "Dialogs", "CodexAuthImportDialog.axaml"));
127-
128-
Assert.DoesNotContain("<ui:CsButton", modelEditorDialog);
129-
Assert.DoesNotContain("<ui:CsIconButton", modelEditorDialog);
130-
Assert.Contains("<cui:CodexIconButton Grid.Column=\"1\"", modelEditorDialog);
131-
Assert.Contains("Variant=\"Ghost\"", modelEditorDialog);
132-
Assert.Contains("Command=\"{Binding CloseModelDialogCommand}\"", modelEditorDialog);
133-
Assert.Contains("Command=\"{Binding SaveModelCommand}\"", modelEditorDialog);
134-
Assert.Contains("<cui:CodexButton Variant=\"Outline\"", modelEditorDialog);
135-
Assert.Contains("<cui:CodexButton.LeadingIcon>", modelEditorDialog);
136-
137-
Assert.DoesNotContain("<ui:CsButton", importDialog);
138-
Assert.DoesNotContain("<ui:CsIconButton", importDialog);
139-
Assert.Contains("<cui:CodexIconButton Grid.Column=\"1\"", importDialog);
140-
Assert.Contains("Variant=\"Ghost\"", importDialog);
141-
Assert.Contains("Command=\"{Binding CancelCodexAuthImportCommand}\"", importDialog);
142-
Assert.Contains("Command=\"{Binding ImportCodexAuthJsonCommand}\"", importDialog);
143-
Assert.Contains("<cui:CodexButton Grid.Column=\"1\"", importDialog);
144-
Assert.Contains("<cui:CodexButton.LeadingIcon>", importDialog);
145-
}
146-
147-
private static string FindRepoFile(string part1, string part2, string part3, string part4, [CallerFilePath] string sourceFile = "")
148-
{
149-
var parts = new[] { part1, part2, part3, part4 };
150-
var sourceDirectory = Path.GetDirectoryName(sourceFile) ?? "";
151-
foreach (var start in new[] { sourceDirectory, Directory.GetCurrentDirectory(), AppContext.BaseDirectory })
152-
{
153-
var directory = new DirectoryInfo(start);
154-
while (directory is not null)
155-
{
156-
var candidate = Path.Combine(new[] { directory.FullName }.Concat(parts).ToArray());
157-
if (File.Exists(candidate))
158-
{
159-
return candidate;
160-
}
161-
162-
directory = directory.Parent;
163-
}
164-
}
165-
166-
throw new FileNotFoundException("Could not find repo file.", Path.Combine(parts));
36+
var repoRoot = FindRepoDirectory();
37+
var viteConfig = File.ReadAllText(Path.Combine(repoRoot, "CodexSwitch.AdminWeb", "vite.config.ts"));
38+
var routerSource = File.ReadAllText(Path.Combine(repoRoot, "CodexSwitch.AdminWeb", "src", "app", "router.tsx"));
39+
var webHostSource = File.ReadAllText(Path.Combine(repoRoot, "CodexSwitch", "WebHost", "AdminWebHost.cs"));
40+
41+
Assert.Contains("base: '/'", viteConfig);
42+
Assert.DoesNotContain("basename=\"/admin\"", routerSource);
43+
Assert.DoesNotContain("/admin/", webHostSource);
44+
Assert.Contains("http://127.0.0.1:5173/", webHostSource);
16745
}
16846

169-
private static string FindRepoDirectory(string part1, string part2, [CallerFilePath] string sourceFile = "")
47+
private static string FindRepoDirectory(string? child = null, [CallerFilePath] string sourceFile = "")
17048
{
171-
var parts = new[] { part1, part2 };
17249
var sourceDirectory = Path.GetDirectoryName(sourceFile) ?? "";
17350
foreach (var start in new[] { sourceDirectory, Directory.GetCurrentDirectory(), AppContext.BaseDirectory })
17451
{
17552
var directory = new DirectoryInfo(start);
17653
while (directory is not null)
17754
{
178-
var candidate = Path.Combine(new[] { directory.FullName }.Concat(parts).ToArray());
179-
if (Directory.Exists(candidate))
55+
if (File.Exists(Path.Combine(directory.FullName, "CodexSwitch.slnx")))
18056
{
181-
return candidate;
57+
var candidate = child is null ? directory.FullName : Path.Combine(directory.FullName, child);
58+
if (Directory.Exists(candidate))
59+
return candidate;
18260
}
18361

18462
directory = directory.Parent;
18563
}
18664
}
18765

188-
throw new DirectoryNotFoundException($"Could not find repo directory: {Path.Combine(parts)}");
66+
throw new DirectoryNotFoundException(child is null ? "Could not find repo directory." : $"Could not find repo directory: {child}");
18967
}
19068
}

0 commit comments

Comments
 (0)