Importing and handling of Apple Keynote documents

Viewed 23

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?

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