unidecompiler-gui-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: unidecompiler-gui-sdk
3
+ Version: 0.1.0
4
+ Summary: Stable read-only plugin API for unidecompiler-gui
5
+ Author-email: Wker <1670133844@qq.com>
6
+ License-Expression: AGPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/Wker666/unidecompiler
8
+ Project-URL: Repository, https://github.com/Wker666/unidecompiler
9
+ Project-URL: Issues, https://github.com/Wker666/unidecompiler/issues
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+
13
+ # unidecompiler-gui-sdk
14
+
15
+ Stable, read-only API for trusted `unidecompiler-gui` Python plugins. The SDK
16
+ does not depend on Qt, core internals, frontends, or the simulator implementation.
17
+
18
+ Install it directly when developing a plugin:
19
+
20
+ ```sh
21
+ python -m pip install unidecompiler-gui-sdk
22
+ ```
23
+
24
+ End users do not need to install it separately: `unidecompiler-gui` depends on
25
+ the matching SDK API.
26
+
27
+ ## Minimal Plugin
28
+
29
+ Place `plugin.toml` and the declared entry module in one directory:
30
+
31
+ ```toml
32
+ [plugin]
33
+ id = "example.workspace-inspector"
34
+ name = "Workspace Inspector"
35
+ version = "1.0.0"
36
+ api = "1"
37
+ entry = "workspace_inspector:register"
38
+
39
+ [python]
40
+ requires = []
41
+ ```
42
+
43
+ ```python
44
+ from unidecompiler_gui_sdk import Command, Panel, PanelState
45
+
46
+
47
+ def register(context):
48
+ context.panels.register(Panel("summary", "Summary"))
49
+
50
+ def refresh(plugin_context):
51
+ document = plugin_context.active_document
52
+ text = "No active document" if document is None else document.display_name
53
+ plugin_context.set_panel_state("summary", PanelState.text_view(text))
54
+
55
+ context.commands.register(Command("refresh", "Refresh summary", refresh))
56
+ context.subscribe("document_selected", lambda _document: refresh(context))
57
+ ```
58
+
59
+ The plugin entry function receives a plugin-scoped context. Plugins are trusted
60
+ in-process code, are not sandboxed, and must not import Qt or private
61
+ decompiler/simulator modules. See the repository's
62
+ `docs/GUI_PLUGIN_DEVELOPMENT.md` for the complete contract.
63
+
64
+ The GUI calls the manifest entry function with a plugin-scoped `PluginContext`.
65
+ Context snapshots are frozen data and navigation/simulation are host requests.
66
+ Register extensions through `context.commands.register(Command(...))` and
67
+ `context.panels.register(Panel(...))`; panels use `PanelState` data rather than
68
+ Qt widgets. The SDK exposes no generic IR, decoded artifacts, frontends, Qt
69
+ objects, or simulator execution internals.
70
+
71
+ For simulation, request frontend-owned targets with
72
+ `request_simulation_targets(document_id)`. The asynchronous target job returns
73
+ `SimulationTargetSnapshot` values containing a display label, parameter names,
74
+ and an opaque query. Pass that query unchanged to `submit_simulation`. Finished
75
+ jobs contain SDK-owned `SimulationResultSnapshot` and
76
+ `SimulationEventSnapshot` data, never simulator implementation objects.
77
+
78
+ `context.settings` stores JSON-compatible values under the current plugin ID;
79
+ one plugin cannot address another plugin's settings namespace.
@@ -0,0 +1,67 @@
1
+ # unidecompiler-gui-sdk
2
+
3
+ Stable, read-only API for trusted `unidecompiler-gui` Python plugins. The SDK
4
+ does not depend on Qt, core internals, frontends, or the simulator implementation.
5
+
6
+ Install it directly when developing a plugin:
7
+
8
+ ```sh
9
+ python -m pip install unidecompiler-gui-sdk
10
+ ```
11
+
12
+ End users do not need to install it separately: `unidecompiler-gui` depends on
13
+ the matching SDK API.
14
+
15
+ ## Minimal Plugin
16
+
17
+ Place `plugin.toml` and the declared entry module in one directory:
18
+
19
+ ```toml
20
+ [plugin]
21
+ id = "example.workspace-inspector"
22
+ name = "Workspace Inspector"
23
+ version = "1.0.0"
24
+ api = "1"
25
+ entry = "workspace_inspector:register"
26
+
27
+ [python]
28
+ requires = []
29
+ ```
30
+
31
+ ```python
32
+ from unidecompiler_gui_sdk import Command, Panel, PanelState
33
+
34
+
35
+ def register(context):
36
+ context.panels.register(Panel("summary", "Summary"))
37
+
38
+ def refresh(plugin_context):
39
+ document = plugin_context.active_document
40
+ text = "No active document" if document is None else document.display_name
41
+ plugin_context.set_panel_state("summary", PanelState.text_view(text))
42
+
43
+ context.commands.register(Command("refresh", "Refresh summary", refresh))
44
+ context.subscribe("document_selected", lambda _document: refresh(context))
45
+ ```
46
+
47
+ The plugin entry function receives a plugin-scoped context. Plugins are trusted
48
+ in-process code, are not sandboxed, and must not import Qt or private
49
+ decompiler/simulator modules. See the repository's
50
+ `docs/GUI_PLUGIN_DEVELOPMENT.md` for the complete contract.
51
+
52
+ The GUI calls the manifest entry function with a plugin-scoped `PluginContext`.
53
+ Context snapshots are frozen data and navigation/simulation are host requests.
54
+ Register extensions through `context.commands.register(Command(...))` and
55
+ `context.panels.register(Panel(...))`; panels use `PanelState` data rather than
56
+ Qt widgets. The SDK exposes no generic IR, decoded artifacts, frontends, Qt
57
+ objects, or simulator execution internals.
58
+
59
+ For simulation, request frontend-owned targets with
60
+ `request_simulation_targets(document_id)`. The asynchronous target job returns
61
+ `SimulationTargetSnapshot` values containing a display label, parameter names,
62
+ and an opaque query. Pass that query unchanged to `submit_simulation`. Finished
63
+ jobs contain SDK-owned `SimulationResultSnapshot` and
64
+ `SimulationEventSnapshot` data, never simulator implementation objects.
65
+
66
+ `context.settings` stores JSON-compatible values under the current plugin ID;
67
+ one plugin cannot address another plugin's settings namespace.
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "unidecompiler-gui-sdk"
7
+ version = "0.1.0"
8
+ description = "Stable read-only plugin API for unidecompiler-gui"
9
+ readme = "README.md"
10
+ license = "AGPL-3.0-or-later"
11
+ authors = [{ name = "Wker", email = "1670133844@qq.com" }]
12
+ requires-python = ">=3.11"
13
+
14
+ [project.urls]
15
+ Homepage = "https://github.com/Wker666/unidecompiler"
16
+ Repository = "https://github.com/Wker666/unidecompiler"
17
+ Issues = "https://github.com/Wker666/unidecompiler/issues"
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """Public, GUI-neutral SDK for trusted unidecompiler GUI plugins."""
2
+
3
+ from .api import (
4
+ AstNodeSnapshot,
5
+ CommandRegistrar,
6
+ Command,
7
+ DocumentSnapshot,
8
+ FunctionSnapshot,
9
+ Panel,
10
+ PanelRegistrar,
11
+ PanelState,
12
+ PluginContext,
13
+ PluginSettings,
14
+ ReferenceSnapshot,
15
+ SelectionSnapshot,
16
+ SimulationJobSnapshot,
17
+ SimulationEventSnapshot,
18
+ SimulationResultSnapshot,
19
+ SimulationTargetJobSnapshot,
20
+ SimulationTargetSnapshot,
21
+ SourceLocation,
22
+ )
23
+
24
+ __all__ = [
25
+ "AstNodeSnapshot", "Command", "CommandRegistrar", "DocumentSnapshot", "FunctionSnapshot",
26
+ "Panel", "PanelRegistrar", "PanelState", "PluginContext", "PluginSettings", "ReferenceSnapshot",
27
+ "SelectionSnapshot", "SimulationEventSnapshot", "SimulationJobSnapshot", "SimulationResultSnapshot",
28
+ "SimulationTargetJobSnapshot", "SimulationTargetSnapshot", "SourceLocation",
29
+ ]
@@ -0,0 +1,202 @@
1
+ """Versioned data contracts shared by GUI plugins and the GUI host."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Callable, Protocol
6
+
7
+
8
+ API_VERSION = "1"
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class SourceLocation:
13
+ frontend: str
14
+ offset: int | None
15
+ line: int | None = None
16
+ detail: str | None = None
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class FunctionSnapshot:
21
+ id: str
22
+ name: str
23
+ status: str
24
+ params: tuple[str, ...] = ()
25
+ source: SourceLocation | None = None
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class AstNodeSnapshot:
30
+ id: str
31
+ kind: str
32
+ source: SourceLocation | None = None
33
+ children: tuple["AstNodeSnapshot", ...] = ()
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class ReferenceSnapshot:
38
+ kind: str
39
+ name: str
40
+ function_id: str | None
41
+ source: SourceLocation | None = None
42
+ target_ids: tuple[str, ...] = ()
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class DocumentSnapshot:
47
+ id: str
48
+ display_name: str
49
+ status: str
50
+ frontend_id: str | None
51
+ revision: int
52
+ functions: tuple[FunctionSnapshot, ...] = ()
53
+ pseudocode: str = ""
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class SelectionSnapshot:
58
+ document_id: str | None = None
59
+ function_id: str | None = None
60
+ source: SourceLocation | None = None
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class SimulationEventSnapshot:
65
+ kind: str
66
+ function: str
67
+ block: str | None = None
68
+ detail: str = ""
69
+ source: SourceLocation | None = None
70
+ args: tuple[Any, ...] = ()
71
+ values: tuple[Any, ...] = ()
72
+ exception: str | None = None
73
+ stdout: str = ""
74
+ stderr: str = ""
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class SimulationResultSnapshot:
79
+ status: str
80
+ values: tuple[Any, ...] = ()
81
+ exception: str | None = None
82
+ cause: str | None = None
83
+ locals: tuple[tuple[str, Any], ...] = ()
84
+ steps: int = 0
85
+ diagnostic: str | None = None
86
+ events: tuple[SimulationEventSnapshot, ...] = ()
87
+ trace_truncated: bool = False
88
+
89
+
90
+ @dataclass(frozen=True)
91
+ class SimulationTargetSnapshot:
92
+ """Presentation-safe simulator target with a frontend-owned opaque query."""
93
+
94
+ label: str
95
+ query: object
96
+ params: tuple[str, ...] = ()
97
+
98
+
99
+ @dataclass(frozen=True)
100
+ class SimulationTargetJobSnapshot:
101
+ id: str
102
+ document_id: str
103
+ revision: int
104
+ status: str
105
+ targets: tuple[SimulationTargetSnapshot, ...] = ()
106
+ diagnostic: str | None = None
107
+ stale: bool = False
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class SimulationJobSnapshot:
112
+ id: str
113
+ document_id: str
114
+ revision: int
115
+ status: str
116
+ result: SimulationResultSnapshot | None = None
117
+ diagnostic: str | None = None
118
+ stale: bool = False
119
+
120
+
121
+ @dataclass(frozen=True)
122
+ class PanelState:
123
+ kind: str
124
+ columns: tuple[str, ...] = ()
125
+ rows: tuple[tuple[str, ...], ...] = ()
126
+ text: str = ""
127
+
128
+ @classmethod
129
+ def text_view(cls, text: str) -> "PanelState":
130
+ return cls("text", text=text)
131
+
132
+ @classmethod
133
+ def table(cls, columns: tuple[str, ...], rows: tuple[tuple[object, ...], ...]) -> "PanelState":
134
+ return cls("table", columns=columns, rows=tuple(tuple(str(value) for value in row) for row in rows))
135
+
136
+
137
+ CommandCallback = Callable[["PluginContext"], None]
138
+
139
+
140
+ @dataclass(frozen=True)
141
+ class Command:
142
+ id: str
143
+ title: str
144
+ callback: CommandCallback
145
+ shortcut: str | None = None
146
+
147
+
148
+ @dataclass(frozen=True)
149
+ class Panel:
150
+ id: str
151
+ title: str
152
+ state: PanelState = field(default_factory=lambda: PanelState.text_view(""))
153
+
154
+
155
+ class CommandRegistrar(Protocol):
156
+ def register(self, command: Command) -> None: ...
157
+
158
+
159
+ class PanelRegistrar(Protocol):
160
+ def register(self, panel: Panel) -> None: ...
161
+
162
+
163
+ class PluginSettings(Protocol):
164
+ """JSON-compatible settings isolated to the current plugin ID."""
165
+
166
+ def get(self, key: str, default: Any = None) -> Any: ...
167
+ def set(self, key: str, value: Any) -> None: ...
168
+ def delete(self, key: str) -> None: ...
169
+
170
+
171
+ class PluginContext(Protocol):
172
+ @property
173
+ def documents(self) -> tuple[DocumentSnapshot, ...]: ...
174
+
175
+ @property
176
+ def active_document(self) -> DocumentSnapshot | None: ...
177
+
178
+ @property
179
+ def selection(self) -> SelectionSnapshot: ...
180
+
181
+ def get_document(self, document_id: str) -> DocumentSnapshot | None: ...
182
+ def get_function(self, document_id: str, function_id: str) -> FunctionSnapshot | None: ...
183
+ def get_ast(self, document_id: str, function_id: str | None = None) -> AstNodeSnapshot | None: ...
184
+ def get_references(self, document_id: str, function_id: str | None = None) -> tuple[ReferenceSnapshot, ...]: ...
185
+ def focus_source(self, document_id: str, source: SourceLocation) -> bool: ...
186
+ def focus_function(self, document_id: str, function_id: str) -> bool: ...
187
+ def request_simulation_targets(self, document_id: str) -> SimulationTargetJobSnapshot: ...
188
+ def get_target_job(self, job_id: str) -> SimulationTargetJobSnapshot | None: ...
189
+ def submit_simulation(self, document_id: str, query: object, args: tuple[object, ...] = (), runtime_path: str | None = None) -> SimulationJobSnapshot: ...
190
+ def get_job(self, job_id: str) -> SimulationJobSnapshot | None: ...
191
+ def cancel_job(self, job_id: str) -> bool: ...
192
+ def set_panel_state(self, panel_id: str, state: PanelState) -> None: ...
193
+ def subscribe(self, event: str, callback: Callable[[object], None]) -> Callable[[], None]: ...
194
+
195
+ @property
196
+ def commands(self) -> CommandRegistrar: ...
197
+
198
+ @property
199
+ def panels(self) -> PanelRegistrar: ...
200
+
201
+ @property
202
+ def settings(self) -> PluginSettings: ...
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: unidecompiler-gui-sdk
3
+ Version: 0.1.0
4
+ Summary: Stable read-only plugin API for unidecompiler-gui
5
+ Author-email: Wker <1670133844@qq.com>
6
+ License-Expression: AGPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/Wker666/unidecompiler
8
+ Project-URL: Repository, https://github.com/Wker666/unidecompiler
9
+ Project-URL: Issues, https://github.com/Wker666/unidecompiler/issues
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+
13
+ # unidecompiler-gui-sdk
14
+
15
+ Stable, read-only API for trusted `unidecompiler-gui` Python plugins. The SDK
16
+ does not depend on Qt, core internals, frontends, or the simulator implementation.
17
+
18
+ Install it directly when developing a plugin:
19
+
20
+ ```sh
21
+ python -m pip install unidecompiler-gui-sdk
22
+ ```
23
+
24
+ End users do not need to install it separately: `unidecompiler-gui` depends on
25
+ the matching SDK API.
26
+
27
+ ## Minimal Plugin
28
+
29
+ Place `plugin.toml` and the declared entry module in one directory:
30
+
31
+ ```toml
32
+ [plugin]
33
+ id = "example.workspace-inspector"
34
+ name = "Workspace Inspector"
35
+ version = "1.0.0"
36
+ api = "1"
37
+ entry = "workspace_inspector:register"
38
+
39
+ [python]
40
+ requires = []
41
+ ```
42
+
43
+ ```python
44
+ from unidecompiler_gui_sdk import Command, Panel, PanelState
45
+
46
+
47
+ def register(context):
48
+ context.panels.register(Panel("summary", "Summary"))
49
+
50
+ def refresh(plugin_context):
51
+ document = plugin_context.active_document
52
+ text = "No active document" if document is None else document.display_name
53
+ plugin_context.set_panel_state("summary", PanelState.text_view(text))
54
+
55
+ context.commands.register(Command("refresh", "Refresh summary", refresh))
56
+ context.subscribe("document_selected", lambda _document: refresh(context))
57
+ ```
58
+
59
+ The plugin entry function receives a plugin-scoped context. Plugins are trusted
60
+ in-process code, are not sandboxed, and must not import Qt or private
61
+ decompiler/simulator modules. See the repository's
62
+ `docs/GUI_PLUGIN_DEVELOPMENT.md` for the complete contract.
63
+
64
+ The GUI calls the manifest entry function with a plugin-scoped `PluginContext`.
65
+ Context snapshots are frozen data and navigation/simulation are host requests.
66
+ Register extensions through `context.commands.register(Command(...))` and
67
+ `context.panels.register(Panel(...))`; panels use `PanelState` data rather than
68
+ Qt widgets. The SDK exposes no generic IR, decoded artifacts, frontends, Qt
69
+ objects, or simulator execution internals.
70
+
71
+ For simulation, request frontend-owned targets with
72
+ `request_simulation_targets(document_id)`. The asynchronous target job returns
73
+ `SimulationTargetSnapshot` values containing a display label, parameter names,
74
+ and an opaque query. Pass that query unchanged to `submit_simulation`. Finished
75
+ jobs contain SDK-owned `SimulationResultSnapshot` and
76
+ `SimulationEventSnapshot` data, never simulator implementation objects.
77
+
78
+ `context.settings` stores JSON-compatible values under the current plugin ID;
79
+ one plugin cannot address another plugin's settings namespace.
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/unidecompiler_gui_sdk/__init__.py
4
+ src/unidecompiler_gui_sdk/api.py
5
+ src/unidecompiler_gui_sdk.egg-info/PKG-INFO
6
+ src/unidecompiler_gui_sdk.egg-info/SOURCES.txt
7
+ src/unidecompiler_gui_sdk.egg-info/dependency_links.txt
8
+ src/unidecompiler_gui_sdk.egg-info/top_level.txt