app
Adom Desktop - Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Desktop: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
"""Document info and activation commands.
document_info — lightweight metadata about ALL open documents plus active doc details.
activate_document — switch to a specific open document tab by name or type.
"""
import adsk.core
def _document_type_str(doc_type) -> str:
"""Convert a DocumentType enum to a human-readable string."""
if doc_type is None:
return "unknown"
type_map = {
0: "FusionDesign",
1: "FusionDesign",
2: "Drawing",
3: "Electronics",
4: "PCB",
}
try:
val = int(doc_type)
return type_map.get(val, f"Unknown({val})")
except (TypeError, ValueError):
return str(doc_type)
def handle_document_info(app: adsk.core.Application, args: dict) -> dict:
"""Get metadata about all open documents and the active document.
Returns every open document/tab with name, type, and active status,
plus detailed cloud info for the active document. Uses only in-memory
data — no cloud API calls.
This is the command to use when Claude needs to know what tabs are open
and which one is focused, without expensive cloud searches.
"""
# Active workspace
active_workspace = "unknown"
try:
ws = app.executeTextCommand("DebugCommands.ActiveWorkspace")
if ws:
active_workspace = ws.strip()
except Exception:
pass
# Active document name (for isActive flag)
active_doc_name = None
active_doc = None
try:
active_doc = app.activeDocument
if active_doc:
active_doc_name = active_doc.name
except Exception:
pass
# Enumerate ALL open documents
open_documents = []
try:
for i in range(app.documents.count):
doc = app.documents.item(i)
doc_info = {
"name": doc.name,
"isActive": (doc.name == active_doc_name) if active_doc_name else False,
}
try:
doc_info["documentType"] = _document_type_str(getattr(doc, 'documentType', None))
doc_info["documentTypeRaw"] = str(getattr(doc, 'documentType', None))
except Exception:
doc_info["documentType"] = "unknown"
try:
doc_info["isSaved"] = doc.isSaved
except Exception:
pass
# Cloud file info (lightweight — just the cached ID)
try:
df = doc.dataFile
if df:
doc_info["cloudFileId"] = df.id
doc_info["isCloudDocument"] = True
try:
doc_info["cloudProjectName"] = df.parentProject.name
except Exception:
pass
else:
doc_info["isCloudDocument"] = False
except Exception:
doc_info["isCloudDocument"] = False
open_documents.append(doc_info)
except Exception as e:
open_documents = [{"error": f"Failed to enumerate documents: {e}"}]
# Detailed info for the active document
active_detail = {}
if active_doc:
try:
active_detail["documentName"] = active_doc.name
except Exception:
pass
try:
df = active_doc.dataFile
if df:
active_detail["isCloudDocument"] = True
try:
active_detail["cloudFileId"] = df.id
except Exception:
pass
try:
active_detail["documentVersion"] = df.versionNumber
except Exception:
pass
try:
active_detail["cloudProjectName"] = df.parentProject.name
except Exception:
pass
try:
# Build folder path
folder = df.parentFolder
parts = []
depth = 0
while folder and depth < 10:
parts.append(folder.name)
try:
folder = folder.parentFolder
except Exception:
break
depth += 1
if parts:
parts.reverse()
active_detail["cloudFolderPath"] = "/".join(parts)
except Exception:
pass
else:
active_detail["isCloudDocument"] = False
except Exception:
active_detail["isCloudDocument"] = False
try:
active_detail["isSaved"] = active_doc.isSaved
except Exception:
pass
# Product type
try:
product = app.activeProduct
if product:
active_detail["productType"] = product.productType
except Exception:
pass
data = {
"activeWorkspace": active_workspace,
"activeDocument": active_doc_name,
"openDocuments": open_documents,
"openDocumentCount": len(open_documents),
**active_detail,
}
return {
"success": True,
"output": f"{len(open_documents)} document(s) open, "
f"active: {active_doc_name or '(none)'}, "
f"workspace: {active_workspace}",
"data": data,
"_hint": "Use fusion_get_app_state to check workspace type. "
"For exports: fusion_export_gerbers, fusion_export_bom, fusion_export_cpl, "
"fusion_export_source (.fsch/.fbrd), fusion_export_eagle_source (.sch/.brd). "
"Use fusion_activate_document to switch tabs.",
}
def handle_activate_document(app: adsk.core.Application, args: dict) -> dict:
"""Switch to a specific open document tab by name or type.
Searches all open documents and activates the matching one. This is
essential for automation — Claude can switch between schematic, board,
and library tabs without asking the user to click.
Args:
name: Document name to activate (case-insensitive substring match).
documentType: Document type to activate — "Electronics", "PCB",
"FusionDesign", "Drawing". If both name and type are
given, both must match.
If multiple documents match, activates the first match and lists all
matches in the response so Claude can refine.
"""
target_name = args.get("name", "").strip().lower()
target_type = args.get("documentType", "").strip().lower()
target_data_file = args.get("dataFileName", "").strip() # EXACT match on dataFile.name
if not target_name and not target_type and not target_data_file:
return {
"success": False,
"error": "Specify 'name' (substring) or 'dataFileName' (exact cloud-file name) "
"or 'documentType' (Electronics, PCB, FusionDesign, Drawing).",
"_hint": "Call fusion_document_info to list open documents by index, or fusion_open_cloud_file to open a new document, then retry.",
}
# Find matching documents
matches = []
try:
for i in range(app.documents.count):
doc = app.documents.item(i)
name_match = True
type_match = True
datafile_match = True
if target_data_file:
df_name = None
try:
df_name = doc.dataFile.name if doc.dataFile else None
except Exception:
df_name = None
datafile_match = (df_name == target_data_file)
if target_name:
name_match = target_name in doc.name.lower()
if target_type:
doc_type_str = _document_type_str(getattr(doc, 'documentType', None)).lower()
doc_type_raw = str(getattr(doc, 'documentType', None)).lower()
type_match = (target_type in doc_type_str or
target_type in doc_type_raw)
if name_match and type_match and datafile_match:
matches.append(doc)
except Exception as e:
return {
"success": False,
"error": f"Failed to enumerate documents: {e}",
"_hint": "Call fusion_get_app_state to check if Fusion is in a usable state; a modal dialog may be blocking.",
}
if not matches:
# List what IS open for debugging
available = []
try:
for i in range(app.documents.count):
doc = app.documents.item(i)
available.append({
"name": doc.name,
"type": _document_type_str(getattr(doc, 'documentType', None)),
})
except Exception:
pass
return {
"success": False,
"error": f"No open document matching name='{target_name or '*'}' "
f"type='{target_type or '*'}'",
"_hint": "Call fusion_document_info to list open documents by index, or fusion_open_cloud_file to open a new document, then retry.",
"data": {
"openDocuments": available,
"hint": "Use fusion_document_info to see all open documents.",
},
}
# Activate the first match
target_doc = matches[0]
try:
activated = target_doc.activate()
except Exception as e:
return {
"success": False,
"error": f"Failed to activate '{target_doc.name}': {e}",
"_hint": "Call fusion_get_app_state to check if Fusion is in a usable state; a modal dialog may be blocking.",
"data": {"hint": "The document may be in a state that prevents activation."},
}
# Query workspace after activation
new_workspace = "unknown"
try:
ws = app.executeTextCommand("DebugCommands.ActiveWorkspace")
if ws:
new_workspace = ws.strip()
except Exception:
pass
response = {
"success": True,
"output": f"Activated '{target_doc.name}' — workspace: {new_workspace}",
"data": {
"activatedDocument": target_doc.name,
"documentType": _document_type_str(getattr(target_doc, 'documentType', None)),
"activeWorkspace": new_workspace,
},
}
# If multiple matches, list them all so Claude can refine
if len(matches) > 1:
response["data"]["allMatches"] = [
{"name": m.name, "type": _document_type_str(getattr(m, 'documentType', None))}
for m in matches
]
response["data"]["hint"] = (
f"{len(matches)} documents matched — activated the first one. "
"Use a more specific name to target a different one."
)
return response