Skip to content

feat: field switches in Settings → Fields (#806) - #1036

Merged
KnifeLemon merged 6 commits into
mainfrom
feat/field-switches
Nov 3, 2026
Merged

KnifeLemon merged 6 commits into
mainfrom
feat/field-switches

Conversation

@SeoJCalive

@SeoJCalive SeoJCalive commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

What does this change?

Part of #806. This connects the field switches from docs/FORMAT-RULES.md (rules 2-4 and 7) to the app. The maintainer agreed to per-field switches. How settings migrate and what happens with older versions was posted in the issue before starting, and the maintainer approved it.

  • New "Fields" page in Settings, after Presets. It has one switch per field that can be turned off (12), and each row lists example formats the switch can hide. A format that also belongs to another field stays visible while that field is on, and one in a field that can't be turned off (TGA in Image, DXF in Vector) is never listed. A new install starts with every field off except Developer. Existing users start with everything on.
  • Donut: a slot whose target format belongs only to turned-off fields is left out, on both faces. This covers default slots, slots the user added, and children inside groups. A format that also belongs to a field that's on stays.
  • Dropped files keep their own fields (rule 4): the outputs of every dropped file's fields stay, for several files and for the mixed donut too.
  • Lists: the toolbar editor's palette (every tab and search), the preset library (including the add-template menu) and the preset editor's target list follow the switches.
  • Docs: the status note at the top of docs/FORMAT-RULES.md now says the field switches (Settings → Fields) and the new default donuts are on screen.
  • Not changed: saved profiles and presets (rule 7: hidden slots are filtered when shown, never deleted), file recognition, and the CLI, which still converts to any format.

What we decided, and what we'd like your opinion on

These choices are ours, not part of the agreed direction. Tell us if you'd rather have them another way.

  1. Remaining slots close up. When a slot is hidden, the others fill the circle, and a group whose children are all hidden isn't drawn. Leaving an empty gap instead would mean changing drawing, hit testing, keyboard moves, the group ring and the flip animation. Would you prefer the gap?
  2. Lists that rule 3 doesn't name are unchanged: the watch-folder preset picker and pattern completion, profile extension completion, the mixed donut's extension list, and the group editor's add list. The extension lists choose inputs, and hiding a watch rule's current preset would make it unclear what the rule does. Should any of them follow the switches?
  3. The switches have their own page. They affect both the toolbar page and the presets page, and 12 rows with example formats don't fit above the three-column toolbar editor. Happy to move them.

Two behaviours to know about:

  • A preset whose target is already hidden, opened in the editor (for example by right-clicking a slot shown under rule 4): the target isn't added to the list. The box shows it as a dimmed placeholder with a note underneath, and the original target is saved unless you pick another one. Before this change, a target missing from the list was silently replaced by Targets[1].
  • The donut drawn in the toolbar editor isn't filtered. It edits the saved profile, so hidden slots must still be movable and removable there.

Type of change

  • New feature or improvement

Existing users

  • The switches are one new property in settings.json, turnedOffFields (field ids). An empty list or a missing property means every field is on. The schema stays at 58, the same way as UiScale, NavCollapsed and MainWindow.
  • A new install is detected by settings.json not existing. A settings file that can't be read (corrupt JSON, null) keeps every field on.
    • The limit of that signal: deleting only settings.json (or pointing FILEE_DATA_DIR at a folder with profiles but no settings) also counts as a new install. The existing slots stay, but 11 fields start off.
  • Older Filee: an older version ignores the property and drops it when it saves its settings. Back on this version, every field is on again. Slots are stored separately, so nothing is lost.

How this was checked:

  • Tests: a schema-58 settings file starts with every field on and keeps its library unchanged.
  • A real round trip, with this branch on cbb3be9 (the settings code is the same on 47f24fa): this branch saved, Filee at 17e2290 saved, then this branch read again. turnedOffFields came back empty, and profiles.json was byte-identical (same sha256) throughout. The app then started on that folder and saved turnedOffFields: [].

How was it tested?

Everything below was run on this branch on top of 47f24fa9, except the mutation checks.

  • dotnet format --verify-no-changes
  • dotnet test. On Linux I ran Core (933/933), CLI (79/79), and the App tests for fields, donut, radial view model, toolbar, presets, localization, render and accessible names (1027/1028). I didn't run the whole App suite locally; CI covers it.
    • The one failure is RenderTests.Engines_page_shows_versions_descriptions_and_download_speed. The capture machine has a system Ghostscript and LibreOffice, so the engines page shows them as installed. This PR doesn't touch engines.
  • Build warnings: 16, the same list as before this change, none of them in a file this PR touches.
  • CLI by hand on this branch (on 47f24fa), with video-pro turned off: filee convert a.srt --to ass converted (exit 0), and filee formats srt still lists ass.

Tests don't assume particular default slots. They put their own slots and test-field-* presets on a donut, or look up a case in whatever default layout exists, so they pass with the default layout #972 brought in, as they did before it.

Mutation checks, run before the rebase onto #972 (on 328225c9). The rebases onto #972 and 47f24fa9 changed no code or test in this PR (the CLI test only sits next to #1018's tests now). Each change was applied on its own and reverted, and each failed the tests listed:

Change Result
No defaults on a new install / defaults applied to existing files new-install / schema-58 tests fail
Corrupt settings treated as a new install both unreadable-file cases fail
Top-level slot filter removed / only to- ids filtered default, user and controlled-layout tests fail
Back face filtered with an empty set / front uses the previous load's set user-slot and second-load tests fail
Hidden if any of the format's fields is off the overlapping-format test fails (DXF, also in Vector)
Dropped files' fields not exempt / only the first file / not on the mixed donut font, multi-file and mixed-donut tests fail
Slots deleted from the profile instead of filtered the re-enable test fails (YAML, MOBI don't come back)
Switches read once when the donut is created layout tests fail on the second load
Palette, library or target list not filtered the C8 tests fail
Editor shows the hidden target as selected / picking a target doesn't change it hidden-target test fails
The CLI refuses a target whose fields are all off the CLI conversion test fails ("Filee reads ASS but can't write it")
fields.name.game removed from all 19 language files only the field-name test fails; the existing key comparison still passes
Fields page lists formats that are also in a field that can't be turned off the example-formats test fails (DXF under CAD)
"Always shown" sentence removed from the page the page test fails
Switches lose their accessible name the accessible-name test for the Fields page fails

Screenshots (UI changes)

Rendered headlessly on this branch (47f24fa9 + this PR; the groups image on cbb3be9b, with the same donut drawing code), by the tests except where noted. Slots drawn grey are ones the dropped files can't be converted with, the existing rule, not the switches.

Fields page (new-install defaults: only Developer is on). CAD lists DWG, STL, OBJ, 3MF (no DXF) and Games lists DDS (no TGA).

page-fields-en
Korean Arabic (right to left)
image page-fields-ar

Donut

Developer off Developer on
donut-fields-developer-off donut-fields-developer-on
Groups, Fonts off TTF dropped, new install
donut-fields-groups donut-fields-font-new-install
JPG + TTF + GLB dropped, new install
donut-fields-mixed
  • Developer off / on: a donut with PNG, YAML, JPG, TOML, WEBP slots. With Developer off, YAML and TOML are left out and the other three close up. With it back on, all five return in their saved order.
  • Groups: Fonts off, on a donut with GIF, A, BMP, C, D, TIFF (B was between GIF and A). B (font children only) is gone, A keeps its one PNG child, C was already empty and stays grey, and D keeps both children (ring open: JPG, WEBP). Same setup as the group test, with three plain slots added so the ring has a normal size.
  • TTF dropped: on a new install Fonts is off, but the font donut still offers WOFF2, WOFF and OTF (rule 4).
  • JPG + TTF + GLB: WOFF2 and GLB stay because the dropped files are fonts and 3D models, and the KML slot (Maps) is gone.

Lists

Available presets, Font tab, Fonts off Fonts on Preset editor, Fonts off
toolbar-palette-font-tab-off toolbar-palette-font-tab-on preset-editor-hidden-target
  • Available presets (the toolbar editor's palette, cropped): with Fonts off, TTF, OTF, WOFF and WOFF2 are gone, and EOT stays because it is also in Old formats, which is on.
  • Preset editor: editing a TTF preset with Fonts off, "Convert to" shows TTF dimmed with the note under it, and TTF isn't in the list. The edit window a donut slot opens (WindowService.EditPresetAsync) shows the same placeholder and note.

Checklist

  • New UI strings are in all src/Filee.App/Assets/i18n/*.json files (18 keys, 19 languages)
    • English and Korean are written by hand. The other 17 languages are machine translated, using the words each language's UI already has for the donut, its slices and "Available presets". A native speaker's check is welcome.
  • New third-party code or binaries are listed in THIRD-PARTY-NOTICES.md (none added)
  • The PR does one thing (unrelated changes go in a separate PR)

AppSettings gets TurnedOffFields, the ids of the fields from
docs/FORMAT-RULES.md rule 2 that the user turned off. An empty list or a
missing property means every field is on, so settings from before it
need no schema step and the schema stays at 58.

UserDataStore.Load tells a new install by settings.json not existing,
and only then turns off every field that can be turned off except
Developer. A settings file that can't be read belongs to an existing
user and keeps every field on; a null list reads as empty. A Filee that
doesn't know the property drops it when it saves its settings, and every
field comes back on; slots are stored separately and stay as they are.

The CLI reads the same settings and still converts to every format.

Part of #806
A new Settings > Fields page, after Presets, has one switch per field
that can be turned off, each with example formats the switch can hide.
A switch saves at once and leaves field ids this version doesn't know in
the list.

A slot whose target belongs only to turned-off fields is left out of
both faces of the donut, a group's children included. A group left with
no children isn't drawn, and the other slots close up. The fields of
every dropped file stay shown (rule 4), on the mixed donut too. Saved
profiles and presets aren't changed (rule 7).

Available presets on the Donut toolbar page, the preset list with its
ready-made presets, and the preset editor's target list follow the
switches. A preset whose target is already hidden keeps it: the editor
shows it as a dimmed placeholder with a note and saves it unless another
target is picked, instead of silently falling back to the second entry.

The 18 new strings are in all 19 languages. English and Korean are
written by hand; the others are machine translated with the words each
language's UI already uses for the donut, its slices and Available
presets.

Part of #806
The status note at the top of FORMAT-RULES.md still said both were to
come.

Part of #806
@SeoJCalive SeoJCalive added macos Run the macOS packages and tests in CI for this pull request feature New features labels Nov 3, 2026

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeoJCalive 꼼꼼한 PR 고마워요. #806에서 합의한 방향 그대로 잘 붙였어요.

  • 규칙 3·4·7을 코드에서 직접 확인했어요. 슬롯은 저장된 프로필에서 지우지 않고 보여 줄 때만 걸러요. 놓은 파일의 분야는 앞면과 뒷면 모두 같은 집합으로 빠져요. 겹치는 형식(DXF, TGA, EOT)은 FormatFields.IsShown 하나로 처리돼요.
  • 새 설치 판단을 settings.json이 있는지로 하는 것도 괜찮아요. 첫 실행에서 FirstRunCompleted가 바로 저장되니까, GUI를 한 번이라도 띄운 사용자는 기존 사용자로 남아요.
  • 목록에 없는 변환 형식을 목록의 첫 형식(Targets[1])으로 몰래 바꾸던 문제까지 같이 고친 점도 좋아요.
  • 변이 테스트 표 덕분에 각 테스트가 무엇을 지키는지 한눈에 보였어요.

로컬(Windows 11, 현재 main과 머지)에서 App 관련 테스트(Field·FieldsPage·Localization·ToolbarEditor·PresetEditor·Radial·AutomationName) 253개는 모두 통과했어요.

고쳐야 할 것

1. main과 합치면 Core 테스트 하나가 실패해요

#995가 방금 머지되면서 스키마가 59가 됐어요. 그래서 UserDataStoreTests.Schema_58_settings_start_with_every_field_on_and_keep_the_library가 깨져요.

  • 스키마 58 설정 파일을 읽으면 이제 59로 올리면서 settings.json을 다시 써요.
  • #995의 59 단계가 Crop video 프리셋을 넣어서 presets.json도 바뀌어요.
  • 그래서 "세 파일이 바이트 단위로 같다"는 단언이 실패해요.
Assert.Equal() Failure: Collections differ
Expected: [123, 34, 115, 99, 104, ···]
Actual:   [123, 13, 10, 32, 32, ···]

이 테스트가 지키려는 건 "turnedOffFields가 없는 기존 설정이면 모든 분야가 켜진다"예요. 스키마 번호와는 상관이 없으니, "schemaVersion":58 대신 AppSettings.CurrentSchemaVersion으로 파일을 쓰고 이름에서 58을 빼면 돼요. 그러면 마이그레이션이 아무것도 바꾸지 않아서 바이트 비교도 그대로 의미가 있어요.

(이 PR은 메인테이너 수정이 꺼져 있어서 제가 직접 올릴 수가 없어요. main을 머지하면서 같이 고쳐 주세요.)

2. 한국어는 해요체로

ko.json은 해요체를 써요(…수 있어요, …나와요). 새 문장 두 개만 합쇼체예요. 이렇게 바꾸면 다른 문장들과 맞아요.

  • fields.description: 쓰지 않는 분야는 꺼 두세요. 꺼진 분야에만 속한 형식은 도넛, 설정 → 원형 툴바의 “사용 가능한 프리셋”, 설정 → 프리셋 목록, 프리셋의 “변환 형식” 목록에서 빠져요. 그 형식의 파일은 계속 알아보고, 명령줄로는 그 형식으로도 변환할 수 있어요. 놓은 파일에는 그 파일 분야의 형식이 그대로 나와요.
  • presets.target_hidden: 현재 변환 형식({0})은 꺼진 분야에만 속해서 목록에 없어요. 다른 형식을 고르지 않으면 그대로 저장돼요.

"세그먼트"는 ko.json에서 도넛 칸을 부르는 말이 아니라서 빼고 "도넛"으로만 썼어요. "대상"도 화면에 보이는 이름이 “변환 형식”이라 그쪽에 맞췄어요.

물어본 세 가지

  1. 남은 칸이 닫히는 것: 지금 방식이 좋아요. 빈칸을 남기면 그리기, 클릭 판정, 키보드, 뒤집기를 모두 건드려야 하는데, 그만한 이득이 없어요.
  2. 규칙 3이 이름을 대지 않은 목록:
    • 그룹 편집기의 추가 목록(GroupEditorViewModel.cs:143)은 스위치를 따라가면 좋겠어요. 도넛에 무엇을 넣을지 고르는 목록이라 툴바 편집기 팔레트와 같은 성격이에요. 거기서만 꺼진 형식이 보이면 어색해요. 이 PR에서 해도 되고, 바로 이어서 작은 PR로 해도 돼요.
    • 나머지는 그대로 두세요: 감시 폴더 프리셋 선택, 확장자 자동 완성, 혼합 도넛의 확장자 목록은 지금 판단 그대로가 맞아요. 확장자는 입력을 고르는 것이고, 감시 규칙의 프리셋을 숨기면 규칙이 무엇을 하는지 알 수 없게 돼요.
  3. 따로 된 페이지: 좋아요. 툴바와 프리셋 두 페이지에 다 영향을 주니 따로 두는 게 맞고, 프리셋 다음 자리도 자연스러워요.

작은 것, 선택

  • FieldsPageViewModel: 끌 수 없는 분야의 이름을 Enum.Parse<FormatCategory>(f.Id, true)로 구해요. 나중에 카테고리가 아닌 분야가 끌 수 없는 쪽에 들어오면 예외가 나요. Enum.TryParse로 바꾸고 실패하면 fields.name.{id}를 쓰면 더 안전해요.

1·2번만 고치고 CI가 초록이면 바로 머지할게요.

The existing-settings test now writes AppSettings.CurrentSchemaVersion, so the
schema 59 step from #995 doesn't rewrite the files it compares. The two new
Korean sentences use the 해요체 of the rest of ko.json, and the Fields page
falls back to fields.name.{id} when an id isn't a format category.

Part of #806
@SeoJCalive

Copy link
Copy Markdown
Collaborator Author

@KnifeLemon 리뷰 고마워요. 1·2번을 고쳐서 올렸어요.

  1. 스키마 59 시험: main(Add Crop video: remove black bars or crop to a ratio (#830) #995 포함)을 머지하고, 시험이 설정 파일을 AppSettings.CurrentSchemaVersion으로 쓰게 바꿨어요. 이름도 Settings_without_turned_off_fields_start_with_every_field_on_and_keep_the_library로 바꿨어요. 기존 파일에도 기본값을 넣도록 코드를 바꾸면 이 시험이 실패하는 것도 확인했어요.
  2. 해요체: fields.description과 presets.target_hidden을 주신 문장 그대로 바꿨어요. 나머지 새 한국어 키는 이름과 라벨이라 그대로예요.
  3. Enum.TryParse: 실패하면 fields.name.{id}를 쓰게 바꿨어요.

그사이 들어온 #817까지 머지한 상태로, 로컬에서 Core 949개, CLI 79개, 관련 App 시험 282개가 모두 통과했어요.

물어본 세 가지 답도 고마워요. 칸이 닫히는 방식과 따로 된 페이지는 지금대로 두고, 감시 폴더·확장자 목록도 그대로 둘게요. 그룹 편집기의 추가 목록은 이 PR이 머지되면 바로 작은 PR로 이어서 올릴게요.

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeoJCalive 빠르게 고쳐 줘서 고마워요. 세 가지 모두 확인했어요.

  1. 스키마 시험: 설정 파일을 AppSettings.CurrentSchemaVersion으로 쓰게 바뀌어서, 앞으로 스키마가 또 올라가도 이 시험은 깨지지 않아요. 이름도 시험 내용과 맞아요.
  2. 해요체: fields.description과 presets.target_hidden 모두 고쳐졌어요.
  3. Enum.TryParse: 카테고리가 아닌 분야는 fields.name.{id}로 나와요.

CI도 모두 초록이에요.

#1024(Annotate)가 곧 먼저 머지돼요. 그 PR은 스키마 60과 ko.json 등 19개 언어 파일에 키를 더하는데, 로컬에서 둘을 같이 합쳐 보니 충돌 없이 합쳐지고 Core 시험(948개)도 통과했어요. #1024가 들어간 뒤 관련 App 시험을 한 번 더 돌려 보고 바로 머지할게요.

그룹 편집기 추가 목록은 말한 대로 이어서 작은 PR로 부탁해요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features macos Run the macOS packages and tests in CI for this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants