feat(keyboard): add custom skills plus events and navigate Shortcuts

Ship user-defined Shortcut skills, companion Events/Navigate recipes, shared skill/style card chrome, and bump the build to 69.
This commit is contained in:
Rocky
2026-08-13 23:01:43 +08:00
parent 148ff807f2
commit 0a60006d2d
50 changed files with 11612 additions and 251 deletions
+272
View File
@@ -0,0 +1,272 @@
#!/usr/bin/env python3
"""Build the companion Shortcut for the Extract Events skill.
Writes an unsigned binary plist that `shortcuts sign` can notarize.
Workflow (must materialize Shortcut Input as Text first — Split Text's
WFInput/ExtensionInput binding finishes the run without iterating):
Text (Shortcut Input)
→ Split Text (new lines)
→ Repeat Each
→ Split Text (|)
→ Get items 14 (start, end, title, location)
→ If end == ALLDAY
→ Add New Event (all-day)
Otherwise
→ Add New Event (timed)
"""
from __future__ import annotations
import plistlib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
OUT_DIR = ROOT / "OSGKeyboard" / "Resources" / "Shortcuts"
UNSIGNED = OUT_DIR / "OSGExtractEvents.unsigned.shortcut"
SIGNED = OUT_DIR / "OSGExtractEvents.shortcut"
OBJECT_REPLACEMENT = "\ufffc"
TEXT_UUID = "A1B2C3D0-1111-4E00-8A5A-FD7E1B0C396F"
SPLIT_LINES_UUID = "A1B2C3D1-2222-4F11-9A6B-0E8F2C1D4A70"
REPEAT_GROUP = "A1B2C3D2-3333-4E02-B3C4-91F0E6A7B812"
REPEAT_START = "A1B2C3D3-4444-4F13-A4D5-02A1F7B8C923"
SPLIT_FIELDS_UUID = "A1B2C3D4-5555-4014-B5E6-13B2A8C9D034"
START_UUID = "A1B2C3D5-6666-4125-C6F7-24C3B9DAE145"
END_UUID = "A1B2C3D6-7777-4236-D7A8-35D4CAEBF256"
TITLE_UUID = "A1B2C3D7-8888-4347-E8B9-46E5DBF0C367"
LOCATION_UUID = "A1B2C3D8-9999-4458-F9CA-57F6EC01D478"
IF_GROUP = "A1B2C3D9-AAAA-4569-0ADB-6807FD12E589"
IF_START = "A1B2C3DA-BBBB-467A-1BEC-79180E23F69A"
EVENT_ALLDAY_UUID = "A1B2C3DB-CCCC-478B-2CFD-8A291F3407AB"
IF_ELSE = "A1B2C3DC-DDDD-489C-3D0E-9B3A204518BC"
EVENT_TIMED_UUID = "A1B2C3DD-EEEE-49AD-4E1F-AC4B315629CD"
IF_END = "A1B2C3DE-FFFF-4ABE-5F20-BD5C42673ADE"
REPEAT_END = "A1B2C3DF-0000-4BCF-6031-CE6D53784BEF"
def attachment(value: dict) -> dict:
return {"Value": value, "WFSerializationType": "WFTextTokenAttachment"}
def token_string(attachment_value: dict) -> dict:
return {
"Value": {
"attachmentsByRange": {"{0, 1}": attachment_value},
"string": OBJECT_REPLACEMENT,
},
"WFSerializationType": "WFTextTokenString",
}
def shortcut_input_text() -> dict:
return token_string({"Type": "ExtensionInput"})
def action_output_text(output_name: str, output_uuid: str) -> dict:
return token_string(
{
"Aggrandizements": [],
"OutputName": output_name,
"OutputUUID": output_uuid,
"Type": "ActionOutput",
}
)
def action_output_attachment(output_name: str, output_uuid: str) -> dict:
return attachment(
{
"Aggrandizements": [],
"OutputName": output_name,
"OutputUUID": output_uuid,
"Type": "ActionOutput",
}
)
def named_variable_text(variable_name: str) -> dict:
return token_string(
{
"Aggrandizements": [],
"Type": "Variable",
"VariableName": variable_name,
}
)
def if_input(output_name: str, output_uuid: str) -> dict:
return {
"Type": "Variable",
"Variable": {
"Value": {
"Aggrandizements": [],
"OutputName": output_name,
"OutputUUID": output_uuid,
"Type": "ActionOutput",
},
"WFSerializationType": "WFTextTokenAttachment",
},
}
def get_item(uuid: str, name: str, index: int) -> dict:
return {
"WFWorkflowActionIdentifier": "is.workflow.actions.getitemfromlist",
"WFWorkflowActionParameters": {
"UUID": uuid,
"CustomOutputName": name,
"WFInput": action_output_attachment("字段", SPLIT_FIELDS_UUID),
"WFItemSpecifier": "Item At Index",
"WFItemIndex": index,
},
}
def add_event(
uuid: str,
*,
all_day: bool,
include_end: bool,
) -> dict:
parameters = {
"UUID": uuid,
"WFCalendarItemTitle": action_output_text("标题", TITLE_UUID),
"WFCalendarItemLocation": action_output_text("地点", LOCATION_UUID),
"WFCalendarItemDates": True,
"WFCalendarItemStartDate": action_output_text("开始", START_UUID),
"WFCalendarItemAllDay": all_day,
"WFCalendarItemShowComposer": False,
"ShowComposeSheet": False,
"WFShowWhenRun": False,
}
if include_end:
parameters["WFCalendarItemEndDate"] = action_output_text("结束", END_UUID)
return {
"WFWorkflowActionIdentifier": "is.workflow.actions.addnewevent",
"WFWorkflowActionParameters": parameters,
}
def workflow() -> dict:
return {
"WFWorkflowClientVersion": "3600.0.4",
"WFWorkflowClientRelease": "26.0",
"WFWorkflowMinimumClientVersion": 900,
"WFWorkflowMinimumClientVersionString": "900",
"WFWorkflowName": "OSG · 提取日程",
"WFWorkflowHasShortcutInputVariables": True,
"WFWorkflowHasOutputFallback": False,
"WFWorkflowImportQuestions": [],
"WFWorkflowTypes": [],
"WFWorkflowInputContentItemClasses": [
"WFStringContentItem",
"WFRichTextContentItem",
],
"WFWorkflowOutputContentItemClasses": [],
"WFWorkflowNoInputBehavior": {
"Name": "WFWorkflowNoInputBehaviorAskForInput",
"Parameters": {
"ItemClass": "WFStringContentItem",
},
},
"WFWorkflowIcon": {
"WFWorkflowIconGlyphNumber": 59675,
"WFWorkflowIconStartColor": 4286828031,
},
"WFWorkflowActions": [
{
"WFWorkflowActionIdentifier": "is.workflow.actions.gettext",
"WFWorkflowActionParameters": {
"UUID": TEXT_UUID,
"CustomOutputName": "快捷指令文本",
"WFTextActionText": shortcut_input_text(),
},
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.text.split",
"WFWorkflowActionParameters": {
"UUID": SPLIT_LINES_UUID,
"CustomOutputName": "",
"text": action_output_text("快捷指令文本", TEXT_UUID),
"WFTextSeparator": "New Lines",
},
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.repeat.each",
"WFWorkflowActionParameters": {
"UUID": REPEAT_START,
"GroupingIdentifier": REPEAT_GROUP,
"WFControlFlowMode": 0,
"WFInput": action_output_attachment("", SPLIT_LINES_UUID),
},
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.text.split",
"WFWorkflowActionParameters": {
"UUID": SPLIT_FIELDS_UUID,
"CustomOutputName": "字段",
"text": named_variable_text("Repeat Item"),
"WFTextSeparator": "Custom",
"WFTextCustomSeparator": "|",
},
},
get_item(START_UUID, "开始", 1),
get_item(END_UUID, "结束", 2),
get_item(TITLE_UUID, "标题", 3),
get_item(LOCATION_UUID, "地点", 4),
{
"WFWorkflowActionIdentifier": "is.workflow.actions.conditional",
"WFWorkflowActionParameters": {
"UUID": IF_START,
"GroupingIdentifier": IF_GROUP,
"WFControlFlowMode": 0,
"WFCondition": 4,
"WFConditionalActionString": "ALLDAY",
"WFInput": if_input("结束", END_UUID),
},
},
add_event(EVENT_ALLDAY_UUID, all_day=True, include_end=False),
{
"WFWorkflowActionIdentifier": "is.workflow.actions.conditional",
"WFWorkflowActionParameters": {
"UUID": IF_ELSE,
"GroupingIdentifier": IF_GROUP,
"WFControlFlowMode": 1,
},
},
add_event(EVENT_TIMED_UUID, all_day=False, include_end=True),
{
"WFWorkflowActionIdentifier": "is.workflow.actions.conditional",
"WFWorkflowActionParameters": {
"UUID": IF_END,
"GroupingIdentifier": IF_GROUP,
"WFControlFlowMode": 2,
},
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.repeat.each",
"WFWorkflowActionParameters": {
"UUID": REPEAT_END,
"GroupingIdentifier": REPEAT_GROUP,
"WFControlFlowMode": 2,
},
},
],
}
def main() -> None:
OUT_DIR.mkdir(parents=True, exist_ok=True)
UNSIGNED.write_bytes(plistlib.dumps(workflow(), fmt=plistlib.FMT_BINARY))
print(f"wrote {UNSIGNED}")
print(
"sign with: shortcuts sign --mode anyone "
f"--input {UNSIGNED} --output {SIGNED}"
)
if __name__ == "__main__":
main()
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""Build the companion Shortcut for the Navigate skill.
Writes an unsigned binary plist that `shortcuts sign` can notarize.
The host already picked 高德 → 百度 → Apple Maps and passes one URL.
This recipe only materializes Shortcut Input as Text and opens it:
Text (Shortcut Input)
→ Open URLs
"""
from __future__ import annotations
import plistlib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
OUT_DIR = ROOT / "OSGKeyboard" / "Resources" / "Shortcuts"
UNSIGNED = OUT_DIR / "OSGNavigate.unsigned.shortcut"
SIGNED = OUT_DIR / "OSGNavigate.shortcut"
OBJECT_REPLACEMENT = "\ufffc"
TEXT_UUID = "C1D2E3F0-1111-4E00-8A5A-FD7E1B0C396F"
OPEN_UUID = "C1D2E3F1-2222-4F11-9A6B-0E8F2C1D4A70"
def token_string(attachment_value: dict) -> dict:
return {
"Value": {
"attachmentsByRange": {"{0, 1}": attachment_value},
"string": OBJECT_REPLACEMENT,
},
"WFSerializationType": "WFTextTokenString",
}
def shortcut_input_text() -> dict:
return token_string({"Type": "ExtensionInput"})
def action_output_text(output_name: str, output_uuid: str) -> dict:
return token_string(
{
"Aggrandizements": [],
"OutputName": output_name,
"OutputUUID": output_uuid,
"Type": "ActionOutput",
}
)
def workflow() -> dict:
return {
"WFWorkflowClientVersion": "3600.0.4",
"WFWorkflowClientRelease": "26.0",
"WFWorkflowMinimumClientVersion": 900,
"WFWorkflowMinimumClientVersionString": "900",
"WFWorkflowName": "OSG · 导航",
"WFWorkflowHasShortcutInputVariables": True,
"WFWorkflowHasOutputFallback": False,
"WFWorkflowImportQuestions": [],
"WFWorkflowTypes": [],
"WFWorkflowInputContentItemClasses": [
"WFStringContentItem",
"WFURLContentItem",
"WFRichTextContentItem",
],
"WFWorkflowOutputContentItemClasses": [],
"WFWorkflowNoInputBehavior": {
"Name": "WFWorkflowNoInputBehaviorAskForInput",
"Parameters": {
"ItemClass": "WFStringContentItem",
},
},
"WFWorkflowIcon": {
"WFWorkflowIconGlyphNumber": 59723,
"WFWorkflowIconStartColor": 4278222847,
},
"WFWorkflowActions": [
{
"WFWorkflowActionIdentifier": "is.workflow.actions.gettext",
"WFWorkflowActionParameters": {
"UUID": TEXT_UUID,
"CustomOutputName": "快捷指令文本",
"WFTextActionText": shortcut_input_text(),
},
},
{
"WFWorkflowActionIdentifier": "is.workflow.actions.openurl",
"WFWorkflowActionParameters": {
"UUID": OPEN_UUID,
"WFURL": action_output_text("快捷指令文本", TEXT_UUID),
"WFShowWhenRun": False,
},
},
],
}
def main() -> None:
OUT_DIR.mkdir(parents=True, exist_ok=True)
UNSIGNED.write_bytes(plistlib.dumps(workflow(), fmt=plistlib.FMT_BINARY))
print(f"wrote {UNSIGNED}")
print(
"sign with: shortcuts sign --mode anyone "
f"--input {UNSIGNED} --output {SIGNED}"
)
if __name__ == "__main__":
main()