Persisting Data =============== Every plugin instance gets its own :attr:`~forkbit_sdk.BasePlugin.data_dir` — a directory on disk for storing files. Use :meth:`~forkbit_sdk.BasePlugin.read_json` and :meth:`~forkbit_sdk.BasePlugin.write_json` for convenient JSON persistence. Save and load release notes --------------------------- .. code-block:: python def _on_save(self) -> None: version = self.version_input.text().strip() if not version: self.notify("Please enter a version number.", level="warning") return # Load existing notes all_notes = self.read_json("notes.json") # Save this version's notes all_notes[version] = { "text": self.editor.toPlainText(), "published": False, } self.write_json("notes.json", all_notes) self.notify(f"Draft for v{version} saved.", level="success") def on_ready(self) -> None: # ... (UI setup from previous chapter) ... # Load last draft all_notes = self.read_json("notes.json") if all_notes: last_version = list(all_notes.keys())[-1] self.version_input.setText(last_version) self.editor.setPlainText(all_notes[last_version]["text"]) How it works ------------ - :meth:`~forkbit_sdk.BasePlugin.write_json` uses atomic writes (write to temp file, then rename) — safe even if the app crashes mid-save - :meth:`~forkbit_sdk.BasePlugin.read_json` returns an empty ``dict`` if the file doesn't exist yet - Files are stored in ``~/.forkbit/projects//plugins//`` - Each plugin instance has its own directory — two instances of the same plugin in different projects don't share data Custom files ------------ For non-JSON data, use :attr:`~forkbit_sdk.BasePlugin.data_dir` directly: .. code-block:: python # Write a text file path = self.data_dir / "export.md" path.write_text(markdown_content, encoding="utf-8") # Read binary data image_data = (self.data_dir / "icon.png").read_bytes() Next: :doc:`git`.