Skip to content

Instantly share code, notes, and snippets.

@leminlimez
Last active November 3, 2025 17:31
Show Gist options
  • Save leminlimez/ca946510d09a132215a6401b2a876221 to your computer and use it in GitHub Desktop.
Save leminlimez/ca946510d09a132215a6401b2a876221 to your computer and use it in GitHub Desktop.

Tendies Files (PosterBoard wallpapers)

Tendies files store the file structure to be restored to PosterBoard.

There are 2 formats for these:

  1. Container format: a containing folder has the name "container"
  • This restores directly to the app container inside of /var/mobile/Containers/Applications/PosterBoard.app and will keep that file structure.
  • Descriptor UUIDs and wallpaper IDs will not be randomized using this format.
  1. Descriptor format: a containing folder has the name "descriptor" or "descriptors"
  • This restores to descriptors inside the container. Currently, it restores to the 61 folder if on iOS 17.0+ or 59 on iOS 16. If the structure also changes, this may be automatically handled by Nugget in future versions.
  • Descriptor UUIDs and wallpaper IDs will be randomized, preventing overlapping.
    • You can add the word "ordered" to the folder to have the UUIDs ordered in collections.
  • Nugget v7.0 can restore to different extensions based on the folder name:
    • Add "video" or "photos" for Suggested Photos (com.apple.PhotosUIPrivate.PhotosPosterProvider)
    • Add "mercury" for Mercury Poster (com.apple.MercuryPoster)
    • Default is Collections (com.apple.WallpaperKit.CollectionsPoster). Do not add anything to the folder name.
  • It is recommended to use these if you are restoring descriptors to collections since this will be more future proof. Randomization of IDs is also safer.

Batter Files (Templates)

Batter files are similar to tendies files in that they store the file structure to be restored to a given domain. Nugget will restore the files in the structure of the "Container" folder in your batter file.

For PosterBoard, they use the same container/descriptor/descriptors folder format as tendies files. In addition, they also contain a config.json file that tells Nugget what the user can customize.

Header

Batter files include a header that includes basic information about the batter file.

Required info:

  • title - the title of the operation
  • author - your name
  • domain - the domain for where it should be restored to (for PosterBoard, put AppDomain-com.apple.PosterBoard)
    • If you want to use sparserestore, make the domain Sparserestore-<FILEPATH> (ie Sparserestore-/var/Managed Preferences)
  • format_version - the minimum version of the config format
    • the current version for the latest Nugget update is "format_version": "2"
  • options - a list of user-configurable options (see Option Format for more info)
    • if you do not want to have any user-configurable options, just use "options": []
Optional info:
  • description - some text info to show under the title

  • min_version - minimum iOS version supported by the template

  • max_version - maximum iOS version supported by the template

    • format: "min_version": "18.0"
    • you can do just min, just max, none, or both
  • resources - a list of embedded resources to include (ie banner/preview images, etc)

  • previews - a list of preview image file names

    • for them to show, their path must be inside of the resources list
  • preview_layout - how the preview images should be laid out (defaults to "horizontal")

    • Valid types: "horizontal", "stacked"
  • banner_text - some text of a banner

  • banner_stylesheet - the Qt style sheet of the banner

Option Format

Every option requires 3 properties:

  • type - the type of option it is (see the specific option types for the required string)
  • label - what text will be displayed in Nugget
  • files - a list of files relative to the parent of the json file
  • associated_preview (Optional) - the name of the preview image to associate the option with
    • for certain options, it may apply effects (ie color) or hide the image (ie remove/picker)
    • for picker, this tag must be in each option

Identifiers

For Nugget to know what to change inside of .caml files, you can put special identifier tags on the properties that you want to change. To do this, add a nuggetId property to the xml tag and set it equal to any valid string (does not have to be a number).

(You will have to modify the file in a text editor to do this.)

It should look something like this:

<CALayer nuggetId="1" id="#1" allowsEdgeAntialiasing="1" allowsGroupOpacity="1" bounds="0 -28 520 563" contentsFormat="RGBA8" cornerCurve="circular" hidden="0" name="Thing" position="195 365" transform="" zPosition="442">
  <sublayers/>
</CALayer>

To use it, just specify the string value in the identifier tag of the options you create in config.json. The file path should be directly to the .caml file, not the .ca file.

If you want to use the default CoreAnimation identifier instead (the one in the "id" tag), then set use_ca_id to true in the json.

Option Types

Replace Files
"type": "replace"

Properties:

  • button_label (Optional) - what text to display on the import button
  • allowed_files - the types of files and allowed extensions (follows the QT filter format)
  • required - whether or not the user must select a file in order for the template to apply
Set Values
"type": "set"

Properties:

  • identifier - identifier in xml file (corresponds to nuggetId value)

  • use_ca_id (Optional) - whether or not to use the CoreAnimation id instead of nuggetId

  • key - the key/tag of the value to change in the property

  • default_value (Optional) - the default value of the input

  • value_type (Optional) - the type of value

    • Valid types: "integer", "float", "string"
  • setter_type - the type of input the user sees for setting the value

    • Valid types: "textbox", "slider", "toggle", "color_picker"
  • Slider Options:

    • min_value - minimum value allowed for input
      • (Required for slider, optional for others)
    • max_value - maximum value allowed for input
      • (Required for slider, optional for others)
    • step (Optional) - the interval between each slider value
  • Toggle Options:

    • inverted (Optional) - if set to true, values will apply to the file inverted
    • toggle_off_value (Optional) - the value to set when the toggle is off (for non-boolean values)
    • toggle_on_value (Optional) - the value to set when the toggle is on
  • Color Picker Options:

    • sets_opacity (Optional) - whether or not opacity is included
      • this will be automatically inferred if you include 4 values in default_value

Additional Details:

The values should be formatted just like they are in the caml. If it is a float, it should still be in quotes in Nugget, i.e. "1.0".

You can add the tag nuggetOffset to properties in the caml file and Nugget will offset the user's choice by that value when applying. This tag is formatted like a math equation, with x representing the input chosen by the user or x, y, z, and a representing multiple values. Example for position:

<CALayer nuggetId="1" nuggetOffset="(2 * x + 10), (3 * y + 15)" id="#1" allowsEdgeAntialiasing="1" allowsGroupOpacity="1" bounds="0 -28 520 563" contentsFormat="RGBA8" cornerCurve="circular" hidden="0" name="Thing" position="195 365" transform="" zPosition="442">
  <sublayers/>
</CALayer>

Each component is separated by a comma. The nuggetId property is required in order to use equations.

Remove Files/Values
"type": "remove"

Properties:

  • inverted (Optional) - if set to true, the files will only be deleted if the checkbox is unchecked
  • default_value (Optional) - whether the checkbox starts as true or false (will be false by default)
  • identifier (Optional) - identifier in xml file. Only needed if you are removing a property or layer
  • use_ca_id (Optional) - whether or not to use the CoreAnimation id instead of nuggetId

The label property will apply to the checkbox itself.

Picker
"type": "picker"

Properties:

  • options - list of options to show up in the picker. Each option contains a label and list of files
  • allow_multiple_selection (Optional) - whether or not to let the user choose multiple options
  • rename (Optional) - whether or not to rename the files chosen by the user
    • will not work if allow_multiple_selection is true
  • names (Optional, required if rename is true) - the list of new names to rename the files to. Must be in the same order as the list of files in the options.
  • default_value (Optional) - the default value to set for the picker based on the index in the array starting from 0 (ex. "default_value": 0)
    • when allow_multiple_selection is set to true, use "default_values": [0, 1, 2]

When the user selects an option from the picker, all other options will be deleted upon applying.

Bundle Id
"type": "bundle_id"

Properties:

  • None

For AppDomain templates, this allows the user to input a custom app bundle id for the template to restore to. The default value is the bundle id that you put in the domain field after the AppDomain-.

Note: This will also change files that start with the same name.

Example Config

An example format of config.json looks like this:

{
  "title": "My Batter",
  "description": "I love Nugget",
  "author": "lemin",
  "format_version": "2",
  "domain": "AppDomain-com.apple.PosterBoard",
  "options": [
    {
      "type": "replace",
      "label": "Background Image",
      "button_label": "Select Image",
      "allowed_files": "Image Files (*.png)",
      "required": true,
      "files": ["descriptors/UUID/version/1/bg/assets/image.png", "descriptors/UUID/version/1/fg/assets/image2.png"]
    },
    
    {
      "type": "remove",
      "label": "File Thing Visible",
      "files": ["descriptors/UUID2/version/1/bg/assets/file1.png"]
    },
    {
      "type": "remove",
      "label": "Include collection item",
      "files": ["descriptors/UUID3"],
      "inverted": true,
      "default_value": true
    },

    {
      "type": "picker",
      "label": "Choose Things",
      "rename": true,
      "names": ["descriptors/UUID/background.ca", "descriptors/UUID/foreground.ca"],
      "options": [
        {
          "label": "Option 1",
          "files": ["descriptors/UUID/bg1.ca", "descriptors/UUID/fg1.ca"]
        },
        {
          "label": "Option 2",
          "files": ["descriptors/UUID/bg2.ca", "descriptors/UUID/fg2.ca"]
        },
        {
          "label": "Option 3",
          "files": ["descriptors/UUID/bg3.ca", "descriptors/UUID/fg3.ca"]
        }
      ]
    },

    {
      "type": "remove",
      "label": "Show Object",
      "files": ["descriptors/UUID/version/1/fg.ca/main.caml"],
      "inverted": true,
      "default_value": true,
      "identifier": "1"
    },

    {
      "type": "set",
      "label": "Animation Duration",
      "files": ["descriptors/UUID/version/1/fg.ca/main.caml"],
      "setter_type": "slider",
      "min_value": 0.5,
      "max_value": 3.0,
      "step": 0.1,
      "default_value": 1.0,
      "key": "duration",
      "identifier": "2"
    },
    {
      "type": "set",
      "label": "Flip Geometry",
      "files": ["descriptors/UUID/version/1/fg.ca/main.caml"],
      "setter_type": "toggle",
      "inverted": false,
      "default_value": false,
      "key": "geometryFlipped",
      "identifier": "3"
    }
  ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment