Importing and handling of Apple Keynote documents

Viewed 4

I would love to see an option to generate markdown exports of keynote presentations.
I often work on my presentations with a combination of my notes in Obsidian (obviously markdown files) and Claude. Since Claude and other LLMs handle .md much better than presentations (for me always Keynote and almost never PowerPoint), I pieced together an AppleScript (that I typically launch via Keyboard Maestro). But this is (of course) far from a perfect solution.
Do you consider working with .key files an option for Marked in the future?

I'd be curious to see your AppleScript. I've never dug into the Keynote file format, just assumed it was like Pages and too proprietary to be worthwhile.

I have considered adding presentation capabilities to Marked, something along the lines of Deckset, but I've not pursued it yet.

1 Answers

Here you go. (Two caveats: I'm not good in AppleScript - Claude helped..., and the output is OK but not perfect). Here's the AppleScript:
-- Keynote to Markdown Exporter
-- Version: Text + Deduplizierung + einfache Markdown-Bullets + LLM-bewusste visuelle Hinweise

tell application "Keynote Creator Studio"
if not (exists front document) then
display dialog "Bitte zuerst eine Keynote-Präsentation öffnen." buttons {"OK"}
return
end if

set theDocument to front document
set docName to name of theDocument
set slideCount to count of slides of theDocument

set md to "# " & docName & linefeed & linefeed

repeat with i from 1 to slideCount
	set theSlide to slide i of theDocument
	
	set md to md & "---" & linefeed & linefeed
	set md to md & "## Folie " & i & linefeed & linefeed
	
	try
		set textItems to text items of theSlide
		set seenTexts to {}
		
		repeat with t in textItems
			try
				set rawText to object text of t as text
				set cleanText to my trimText(rawText)
				
				if cleanText is not "" then
					if seenTexts does not contain cleanText then
						set end of seenTexts to cleanText
						set md to md & my formatTextBlockAsMarkdown(cleanText) & linefeed & linefeed
					end if
				end if
			end try
		end repeat
	on error errMsg
		set md to md & "_Text konnte nicht gelesen werden: " & errMsg & "_" & linefeed & linefeed
	end try
	
	set visualSummary to my getVisualSummary(theSlide)
	if visualSummary is not "" then
		set md to md & "### Visuelle Elemente" & linefeed & linefeed
		set md to md & visualSummary & linefeed
	end if
	
	try
		set notesText to presenter notes of theSlide as text
		set notesText to my trimText(notesText)
		
		if notesText is not "" then
			set md to md & "### Presenter Notes" & linefeed & linefeed
			set md to md & notesText & linefeed & linefeed
		end if
	end try
end repeat

end tell

set fileName to my cleanFileName(docName) & ".md"
set outPath to ((path to desktop folder as text) & fileName)

set fileRef to open for access file outPath with write permission
set eof of fileRef to 0
write md to fileRef as «class utf8»
close access fileRef

display dialog "Markdown exportiert nach:" & linefeed & outPath buttons {"OK"}

on getVisualSummary(theSlide)
set summaryText to ""

set imageCount to my safeCount("images", theSlide)
set shapeCount to my safeCount("shapes", theSlide)
set tableCount to my safeCount("tables", theSlide)
set chartCount to my safeCount("charts", theSlide)
set groupCount to my safeCount("groups", theSlide)
set movieCount to my safeCount("movies", theSlide)

if imageCount is not "0" then
	set summaryText to summaryText & "- " & imageCount & " Bild/Grafik(en) vorhanden; visueller Inhalt nicht im Markdown enthalten." & linefeed
end if

if tableCount is not "0" then
	set summaryText to summaryText & "- " & tableCount & " Tabelle(n) vorhanden; Tabelleninhalt nicht exportiert." & linefeed
end if

if chartCount is not "0" then
	set summaryText to summaryText & "- " & chartCount & " Diagramm(e) vorhanden; visuelle Daten nicht exportiert." & linefeed
end if

if movieCount is not "0" then
	set summaryText to summaryText & "- " & movieCount & " Video(s) vorhanden; Medieninhalt nicht exportiert." & linefeed
end if

if shapeCount is not "0" then
	set summaryText to summaryText & "- " & shapeCount & " Form(en) vorhanden; visueller Inhalt möglicherweise nicht vollständig im Markdown enthalten." & linefeed
end if

if groupCount is not "0" then
	set summaryText to summaryText & "- " & groupCount & " gruppierte Objekt(e) vorhanden; visueller Inhalt möglicherweise nicht vollständig im Markdown enthalten." & linefeed
end if

return summaryText

end getVisualSummary

on safeCount(objectType, theSlide)
try
tell application "Keynote Creator Studio"
if objectType is "images" then
return (count of images of theSlide) as text
else if objectType is "shapes" then
return (count of shapes of theSlide) as text
else if objectType is "tables" then
return (count of tables of theSlide) as text
else if objectType is "charts" then
return (count of charts of theSlide) as text
else if objectType is "groups" then
return (count of groups of theSlide) as text
else if objectType is "movies" then
return (count of movies of theSlide) as text
else
return "0"
end if
end tell
on error
return "0"
end try
end safeCount

on formatTextBlockAsMarkdown(theText)
set oldDelims to AppleScript's text item delimiters

set AppleScript's text item delimiters to linefeed
set theLines to text items of theText
set AppleScript's text item delimiters to oldDelims

set formattedText to ""
set lineCount to count of theLines

repeat with i from 1 to lineCount
	set thisLine to item i of theLines
	set thisLine to my trimSpaces(thisLine)
	
	if thisLine is "" then
		set formattedText to formattedText & linefeed
	else if i is 1 then
		set formattedText to formattedText & thisLine & linefeed
	else
		set formattedText to formattedText & "- " & thisLine & linefeed
	end if
end repeat

return my trimText(formattedText)

end formatTextBlockAsMarkdown

on cleanFileName(theName)
set badChars to {":", "/", "\", "*", "?", """, "<", ">", "|"}
set cleanName to theName

repeat with c in badChars
	set AppleScript's text item delimiters to c
	set parts to text items of cleanName
	set AppleScript's text item delimiters to "-"
	set cleanName to parts as text
end repeat

set AppleScript's text item delimiters to ""
return cleanName

end cleanFileName

on trimText(theText)
set oldDelims to AppleScript's text item delimiters

set AppleScript's text item delimiters to {return}
set parts to text items of theText
set AppleScript's text item delimiters to linefeed
set cleaned to parts as text

set AppleScript's text item delimiters to oldDelims

repeat while cleaned starts with linefeed
	set cleaned to text 2 thru -1 of cleaned
end repeat

repeat while cleaned ends with linefeed
	set cleaned to text 1 thru -2 of cleaned
end repeat

return cleaned

end trimText

on trimSpaces(theText)
set cleaned to theText

repeat while cleaned starts with " "
	set cleaned to text 2 thru -1 of cleaned
end repeat

repeat while cleaned ends with " "
	set cleaned to text 1 thru -2 of cleaned
end repeat

return cleaned

end trimSpaces