Skip to content

Conversation

@david-allison
Copy link
Member

@david-allison david-allison commented Jan 7, 2026

Purpose / Description

Anki has discoverability problems. One of the features which is unclear to users without reading the manual 'Special Fields': fields which relate to the metadata of the card.

{{Subdeck}} for example lists the current deck

This Pull Request updates the 'insert field' dialog to allows a user to select special fields. If the user came from 'manage note types' then the note type & card template have an example preview. If the user came from the note editor, then fields are populated based on the note ID, and the selected card template.

⚠️ This uses the selected TEMPLATE data to preview, rather than the edited card in the Reviewer. Question my decision
⚠️ I arbitrarily named 'non-special fields' as 'basic'

Approach

  • Move to a ViewModel for simplicity of testing
  • Extend InsertFieldDialog: firstly just listing the special fields in a list
  • Then extend the code to produce a live 'description' of the field when on the tab:

How Has This Been Tested?

Pixel 9 Pro image image image

added template (no ord)
image

API 34 Tablet Emulator Screenshot 2026-01-07 at 17 08 10

Learning

Checklist

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner
    • No suggestions

@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

Important

Maintainers: This PR contains Strings changes

  1. Sync Translations before merging this PR and wait for the action to complete
  2. Review and merge the auto-generated PR in order to sync all user-submitted translations
  3. Sync Translations again and merge the PR so the huge automated string changes caused by merging this PR are by themselves and easy to review

val dialog =
AlertDialog.Builder(requireContext()).create {
title(R.string.card_template_editor_select_field)
negativeButton(R.string.dialog_cancel)
Copy link
Member Author

Choose a reason for hiding this comment

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

maybe remove this?

@david-allison david-allison force-pushed the better-add-field branch 2 times, most recently from 3a6e9e3 to e9cd55b Compare January 7, 2026 10:29
<string name="basic_fields_tab_header" comment="Tab header for inserting non-special fields: {{Front}}">Basic</string>
<string name="special_fields_tab_header" comment="Tab header for inserting special fields: {{Deck}}">Special</string>
<string name="special_field_example_suffix"><![CDATA[: ‘<b>%1$s</b>’]]></string>
<string name="special_field_front_side_help">The front template content. Audio is not automatically played</string>
Copy link
Member Author

Choose a reason for hiding this comment

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

Please nitpick

<string name="import_error_resolution_select_file">Open the file using your device’s file browser app</string>

<!-- Special Fields -->
<string name="basic_fields_tab_header" comment="Tab header for inserting non-special fields: {{Front}}">Basic</string>
Copy link
Member Author

@david-allison david-allison Jan 7, 2026

Choose a reason for hiding this comment

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

Especially this - I feel I'm naming a new concept

EDIT:

The following are all better than 'Basic': I think 'Fields' makes sense

  • Fields
  • Normal
  • Regular
  • Standard

@ZornHadNoChoice
Copy link

Look nice! I think the example tags should be something simpler like "Tag1 Tag2". I prefer "Normal" over "Basic".

@david-allison
Copy link
Member Author

david-allison commented Jan 7, 2026

@ZornHadNoChoice The tags come from the note itself. I'd rather do this, over sending tag1 tag2 to our translators.

Copy link
Contributor

@criticalAY criticalAY left a comment

Choose a reason for hiding this comment

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

Cool and clean

Comment on lines +108 to +114
fun all(side: SingleCardSide) =
ALL.filter { field ->
when {
field == FrontSide && side == FRONT -> false
else -> true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we pre-compute these?

Copy link
Member Author

@david-allison david-allison Jan 15, 2026

Choose a reason for hiding this comment

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

Not worth it IMO. More code for nanoseconds of performance (and that's if the JIT doesn't convert it to a constant lookup)

class InsertFieldDialogViewModel(
savedStateHandle: SavedStateHandle,
) : ViewModel() {
var currentTab: Tab = Tab.BASIC
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there chances it will be killed if the system kills the process to save memory, just want to be sure

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to a flow

Comment on lines 103 to 107
fun String.ellipsize(maxLength: Int): String {
require(maxLength > 1) { "invalid length: $maxLength" }
if (this.length <= maxLength) return this
return this.take(maxLength - 1) + ""
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big concern but what if there is a emoji at max limit as last char, it will be split into 2

Copy link
Member Author

Choose a reason for hiding this comment

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

Added tests and a BreakIterator, ChatGPT for the APIs, myself for the implementation

I will be extending this class to handle field filters and special fields

* rendering the output is now the responsibility of `renderToTemplateTag()`
  rather than done in the CardTemplateEditor
This introduces Anki's 'Special Fields' in the ViewModel
 and adds a 'side' argument, so `{{FrontSide}}` can optionally
 be displayed

This does not implement the user interface

See: https://docs.ankiweb.net/templates/fields.html#special-fields
Improves discoverability of this feature
 vs needing to go through the manual

Assisted-by: GPT-5.2: ViewPager2.updateHeight
This provides an explanation of all Special Fields
 to the user in the 'Insert field' dialog

This also produces contextual help for all fields
based on the context of the selected card/note (if any)

Assisted-by: GPT-5.2 - learning about BreakIterator
@BrayanDSO

This comment was marked as off-topic.

@david-allison

This comment was marked as off-topic.

@BrayanDSO

This comment was marked as off-topic.

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

Labels

Needs Review Review High Priority Request for high priority review Strings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants