JavaScript For Automation 3 - Jesse Shanks
JavaScript For Automation 3 - Jesse Shanks
OceanofPDF.com

OceanofPDF.com
Preface
Using OpenAI API to Generate JXA Code within Script Editor
Mp4 Audio Transcription via GPT
Script to use a Custom GPT to Create a Book Index for a Pages
Document
Calling a Python Script from JXA to interface with OpenAI API
Querying Google Gemini API
Add keywords to selected images in Photos
Reveal a File's Spotlight Information
Convert Numbers Spreadsheet Data to JSON
Use a Custom GPT called "JXA Helper"
Calling to Google's API from Filemaker for Research
Transfer Page from Safari to Google Chrome
Extract Shorter AI-Picked Playlist from Longer
Customer Support
Shortcuts
Generate QR Code
Select Photos to Extract Text into a Note
Creating a quiz with Changeable Questions
Create a Playlist Image with AI
Find a Playlist Image with Google
OceanofPDF.com
Preface
When I was recapping the history of "Scripting" in computing in book 1 of
this series, I gave only scarce mention of Python which i hope to redress in
this third entry of the series.
Python as a Scripting Language
Python is a high-level, interpreted programming language known for its
simplicity, readability, and versatility. As a scripting language, Python excels
in automating tasks, handling system administration, and integrating software
components. Here’s an overview of what makes Python an ideal choice for
scripting:
Key Features of Python
1. Ease of Learning and Use :
- Python's syntax is clear and straightforward, making it accessible for
beginners. Its code is easy to read and write, which reduces the learning curve
and enhances productivity.
2. Interpreted Language :
- Python is an interpreted language, meaning code is executed line-by-line,
allowing for immediate feedback and rapid development cycles. This feature
makes debugging and testing scripts more efficient.
3. Cross-Platform Compatibility :
- Python scripts can run on various operating systems, including Windows,
macOS, and Linux, without requiring modification. This cross-platform nature
ensures that Python scripts are highly portable.
4. Extensive Standard Library :
- Python boasts a comprehensive standard library that provides modules and
functions for various tasks, such as file handling, system operations, internet
protocols, and data manipulation. This rich library reduces the need to write
code from scratch.
5. Large and Active Community :
- Python has a vast and active user community that contributes to a wealth
of third-party modules and packages. This ecosystem supports a wide range of
applications, from web development and data analysis to machine learning
and automation.
6. Integration Capabilities :
- Python can easily integrate with other languages and tools, making it a
versatile choice for scripting in diverse environments. It can interface with
C/C++, Java, and other programming languages, as well as various APIs and
web services.
7. Automation and Scripting :
- Python is widely used for automating repetitive tasks, such as file
manipulation, data extraction, and batch processing. Its ability to script
complex workflows with minimal code makes it a powerful tool for system
administrators and developers.
8. Object-Oriented and Functional Programming :
- Python supports both object-oriented and functional programming
paradigms. This flexibility allows developers to choose the best approach for
their specific needs, enhancing code reusability and maintainability.
Applications of Python Scripting
- System Administration : Automating routine tasks, managing system
configurations, and monitoring system health.
- Web Scraping : Extracting data from websites for analysis or integration
with other systems.
- Data Analysis : Processing and analyzing large datasets with libraries like
pandas and NumPy.
- Web Development : Building web applications using frameworks like
Django and Flask.
- Automation of Software Testing : Writing test scripts to automate the testing
of software applications.
- Task Automation : Automating workflows, such as batch processing of files,
sending emails, and interacting with APIs.
Python’s combination of simplicity, versatility, and powerful libraries makes it
an excellent choice for scripting. Whether you are a beginner looking to
automate small tasks or an experienced developer building complex
automation scripts, Python provides the tools and support needed to get the
job done efficiently.
SwiftUI - the Future?
Not speaking scientifically, but more a musing. I’ve been spending some time
working with SwiftUi and in some ways I think it could be the one that rules
them all. The popular generative AIs can write pretty solid AppleScript, JXA,
and Swift UI code already. I suspect that Apple will have such generation
built-in to Xcode before long and Script Editor to possibly. I already have used
JXA to integrate AI into the iWork apps and BBEdit has a chat worksheet
“Write a JXA script that retrieves cells A1-H16 from numbers and create a
series of Keynote slides from the rows and then writes a presentation
script.” In someways writing swift UI code in the iPad is very similar to
writing advanced Apple Script. And it seems very possible that a souped up
version of Script Editor writing SwiftUI with an ability to talk to apps could
create the kind of tools we want and more. But this is purely speculative and
not based on any specific knowledge.
The release of the Mac Desktop version of ChatGPT might mean that some of
these scripts will require alterations.
OceanofPDF.com

OceanofPDF.com
Using OpenAI API to Generate JXA Code within
Script Editor
In this chapter, we explore a practical example of integrating JavaScript for
Automation (JXA) with OpenAI's API. The goal is to create an automation
script that interacts with OpenAI's powerful GPT-3.5 model, enhancing the
Mac user's scripting capabilities with advanced AI insights.
Setting the Stage
We begin by initializing our JXA environment and setting up the necessary
applications. The script leverages the Script Editor (`se`) application for script
execution and manipulation, along with the current application context (`app`)
to utilize standard scripting additions.
const se = Application("Script Editor");
const app = Application.currentApplication();
app.includeStandardAdditions = true;
Defining User Interaction
The core of our script lies in the `getPresetsText` function, designed to present
the user with a dialog containing preset options. These presets range from
code rewriting to comment generation, reflecting typical scripting tasks that a
user might want to automate.
function getPresetsText() {
// Array of preset options
const presets = ["1 - Rewrite this code to be more efficient",
"2 - Continue this code",
"3 - Write a function with this code",
"4 - Write comments for this code"];
// Displaying the dialog and capturing user input
const response = app.displayDialog(presets.join('\n'), {
defaultAnswer: '4'
});
// Parsing the user's choice and returning the corresponding preset text
const inputNumber = parseInt(response.textReturned);
return presets[inputNumber - 1] || "Invalid input";
}
Preparing the Code for Submission
To ensure the code is suitable for processing by the OpenAI API, a
`cleanString` function is implemented. This function tidies up the string by
replacing newline and tab characters with spaces, making it more suitable for
API transmission.
function cleanString(inputString) {
return inputString.replace(/[\n\t]/g, ' ');
}
OceanofPDF.com
Mp4 Audio Transcription via GPT
This script is designed to automate a sequence of tasks involving video file
conversion and audio transcription using Apple's JavaScript for Automation
(JXA) and external tools like FFmpeg and OpenAI's API. Here's a step-by-
step breakdown with comments explaining each part of the script:
1. Initialize JXA Application Object
app = Application.currentApplication()
app.includeStandardAdditions = true
- Initializes the `Application` object for the current script and includes
standard scripting additions, enabling the use of dialog boxes, file selection
dialogs, etc.
2. Function to Escape Special Characters
function escapeCharacters(str) {
return str.replace(/~/g, '\\~').replace(/&/g, '\\&');
}
- Defines a function `escapeCharacters` to escape special characters (`~`
and `&`) in a string. This is useful for sanitizing file paths or strings that will
be used in shell commands.
3. File Selection Dialog
filePath = app.chooseFile({withPrompt:"Choose an MP4 or M4v Movie
file"})
inFile = filePath.toString()
- Opens a file dialog asking the user to choose an MP4 or M4v movie file.
The selected file path is converted to a string.
4. Extract File Name and Prepare Output Path
filePathParts = inFile.split('/')
fileName = filePathParts[filePathParts.length-1].split('.')
outFile = `/users/homefolder/${fileName[0]}.mp3`
- Splits the chosen file path into parts (separated by `/`), extracts the file
name, and prepares an output file path with `.mp3` extension in the user's
home folder.
5. FFmpeg Command for Audio Extraction
command = `/usr/local/bin/ffmpeg -i ${inFile} ${outFile}`
response = app.doShellScript(command)
- Constructs a command to use FFmpeg to extract audio from the selected
video file and save it as an MP3 file. Executes the command using
`doShellScript`.
6. Display Dialog and Wait for User
holder = app.displayDialog("Wait")
- Displays a dialog with the message "Wait". This is likely meant as a
placeholder for user acknowledgment before proceeding. There is room here
to design a timing based on typical processing speed to delay the transcription
until ffmpeg is done with it's work.
7. Check User Response and Proceed
if (holder.buttonReturned === "OK"){
// API Call and Transcription Logic
} else {
app.displayAlert("No")
}
- Checks if the user clicked "OK" in the dialog. If not, displays an alert with
the message "No".
8. API Call for Transcription
command = "curl --request POST --url
https://api.openai.com/v1/audio/transcriptions " +
"--header 'Authorization: Bearer $OPEN-API-KEY' " +
"--header 'Content-Type: multipart/form-data' " +
"--form file=@" + outFile + " --form model=whisper-1";
try {
jsonObj = app.doShellScript(command);
obj = JSON.parse(jsonObj);
console.log(obj.text)
} catch (error) {
console.log("An error occurred: " + error);
}
app.setTheClipboardTo(obj.text)
- Constructs a `curl` command to send a POST request to the OpenAI audio
transcription API, using the generated MP3 file.
- The script attempts to execute the command, parse the JSON response,
and log the transcription to the console.
- If successful, the transcription is copied to the clipboard. If an error
occurs, it logs the error message.
This script requires the user to have FFmpeg installed and accessible at
`/usr/local/bin/ffmpeg`, and it assumes the OpenAI API token is directly
provided in the script or through some other means. The script performs no
error checking for the file selection or FFmpeg command execution, so it
assumes these operations are successful. Additionally, the API token must be
included in the script, which is necessary for the transcription API call to
work.

OceanofPDF.com
Script to use a Custom GPT to Create a Book Index
for a Pages Document
// Source text from Pages
pages = Application("Pages")
// ChatGPT made into a Safari Web App
gpt = Application("ChatGPT")
// System Events for Key Strokes
var se = Application('System Events')
// Provide access to Clipboard
app = Application.currentApplication()
app.includeStandardAdditions = true
// Front Pages Document
doc = pages.documents[0]
// Pre-prompt
pageContent = "Index the following text using the page number provided: "
// Starting page
startingPage = 3
// Loop ten pages into the content to index
for (i=startingPage;i<startingPage+2;i++){
pageNum = i+1 // Get page number
currentPage = doc.pages[i].bodyText() // Get Body Text of each page
// Compile the pre-prompt, the pages content, the page number
pageContent = pageContent + currentPage + " " + pageNum
}
// Put the data on the clipboard
app.setTheClipboardTo(pageContent)
// Activate the GPT App
gpt.activate()
delay(1)
// Make sure the input text box is focused
se.keyCode(53, { using: ["shift down"] });
delay(1)
/ Paste into ChatGPT You can use "Index Master" a custom GPT at
https://chat.openai.com/g/g-Fv9AmeZvV-index-master /
se.keystroke("v", { using: ["command down"] })
delay(1)
// hit the return key
se.keyCode(76)
/ Once ChatGPT returns the index it can be copied with Shift-command-down
In this system, the different sections will come back with different formats.
This is part of the indeterminate nature of chat replies. The text will need to be
compiled and reformatted. /
This script integrates JavaScript for Automation (JXA) to perform a series of
tasks across different applications on a Mac, specifically targeting Pages,
Safari running a web app version of ChatGPT, and utilizing System Events for
automation.
Here's a step-by-step description of what the script does:
1. Access Pages Application: The script starts by creating an object to interact
with the Pages application, allowing it to manipulate and retrieve data from
Pages documents.
2. Access ChatGPT as a Safari Web App: It then sets up an object to interact
with ChatGPT through a web app version opened in Safari, indicating an
intention to send data to ChatGPT for processing.
3. System Events for Keyboard Strokes: By creating an object for 'System
Events', the script can simulate keyboard inputs, a method often used to
automate interactions that don't have direct API support in scripting
environments.
4. Clipboard Access: It prepares the current application (the script itself) to
include standard additions, which are necessary to interact with the system's
clipboard for copying and pasting data.
5. Retrieve Content from a Pages Document: The script accesses the first
document open in Pages and starts a loop to read the body text from a
specified starting page, here assumed to be the third page (`startingPage = 3`),
and continues for two pages. It compiles this content along with an
incrementing page number into a single string, pre-pended with a custom
message (`"Index the following text using the page number provided: "`).
6. Copy Data to Clipboard: The compiled string, which now includes the text
from the specified pages and their respective page numbers, is copied to the
clipboard.
7. Activate ChatGPT Web App: The script switches focus to the Safari web
app version of ChatGPT, preparing to input the copied text.
8. Keyboard Strokes for Input: It simulates keyboard shortcuts to ensure the
text input area is focused and then pastes the clipboard content into the
ChatGPT input field by simulating a CMD+V (paste) operation.
9. Submit the Query: Finally, the script simulates pressing the return key to
submit the query to ChatGPT.
Once ChatGPT processes the submitted text, the user will manually handle the
response. "Index Master" is a customized ChatGPT model designed for
indexing documents.
This automation could be part of a larger workflow for document
management, where the indexing of text from Pages documents is automated
through ChatGPT's processing capabilities. The script cleverly uses JXA to
bridge the gap between local applications and web services, showcasing the
versatility of automation on macOS.
OceanofPDF.com

OceanofPDF.com
Calling a Python Script from JXA to interface with
OpenAI API
This script comprises two main parts: a JXA (JavaScript for Automation) code
snippet for macOS and a Python script. Let's annotate each part for clarity.
JXA Code Explanation
1. Initialization:
app = Application.currentApplication()
app.includeStandardAdditions = true
- `Application.currentApplication()` initializes the current script to use
macOS's scripting capabilities.
- `app.includeStandardAdditions = true` enables the use of standard
commands like displaying dialogs, running shell scripts, etc.
2. Displaying a Dialog:
response = app.displayDialog("Prompt?", {defaultAnswer: 'a cat in shades
of blue'})
- `app.displayDialog` shows a dialog box to the user. Here, it asks a
question ("Prompt?") and provides a default answer ('a cat in shades of blue').
3. Getting User Input:
parameter = response.textReturned
- `response.textReturned` fetches the text input by the user from the dialog
box.
4. Preparing a Shell Command:
command = "/usr/local/bin/python ~/openaiapi/inp.py '" + parameter + "'"
- Constructs a command to run a Python script located at
`~/openaiapi/inp.py`. The user's input (`parameter`) is passed as an argument
to this script.
5. Executing the Shell Command:
theTry = app.doShellScript(command)
- `app.doShellScript` executes the constructed command in the shell.
6. Opening a URL:
app.openLocation(theTry)
- `app.openLocation` opens a URL. Here, it's expected that the Python
script returns a URL as its output, which is then opened.
Python Code Explanation
1. Imports:
python
from openai import OpenAI
import webbrowser
import sys
- Imports necessary modules: `openai` for OpenAI API interaction,
`webbrowser` to open URLs, and `sys` for command line argument handling.
2. OpenAI Client Initialization:
python
client = OpenAI(api_key='OPEN-AI KEY)
- Initializes the OpenAI client with a specific API key.
3. Command Line Argument Handling:
python
if len(sys.argv) > 1:
parameter = sys.argv[1]
else:
parameter = "A cat in a hat" # Default value if no parameter is
provided
- Checks if a command-line argument is provided (from the JXA script). If
not, uses a default value.
4. Image Generation Request:
python
response = client.images.generate(
model="dall-e-3",
prompt=parameter,
size="1024x1024",
quality="standard",
n=1,
)
- Calls the OpenAI's DALL-E 3 model to generate an image based on the
provided prompt (`parameter`). Specifies the image size, quality, and the
number of images (1 in this case).
5. Retrieving the Image URL:
python
image_url = response.data[0].url
- Extracts the URL of the generated image from the response.
6. Opening the Image in a Web Browser:
python
webbrowser.open(image_url)
- Opens the image URL in the default web browser.
Overall Flow
- The JXA script prompts the user for input, runs a Python script with the inpu
as a parameter, and expects a URL in return.
- The Python script uses the OpenAI API to generate an image based on the
input and opens the resulting image in a web browser.
OceanofPDF.com

OceanofPDF.com
Querying Google Gemini API
app = Application.currentApplication()
app.includeStandardAdditions = true
bebe = Library('bebe')
prompt = "make list of ways to research javescript language"
command = `curl -H 'Content-Type: application/json' -d '{"contents":[{"parts"
[{"text":"${prompt}"}]}]}' -X POST
https://generativelanguage.googleapis.com/v1beta/models/gemini-
pro:generateContent?key=API-KEY`
try {
jsonObj = app.doShellScript(command)
obj = JSON.parse(jsonObj)
geminiText = obj.candidates[0].content.parts[0].text
} catch (error) {
response = app.displayAlert(error.message, {
message: error.message,
as: 'critical'
})
}
bebe.newBBDocWithText ('JSON',geminiText)
The script interacts with an external API, parses the response, and then uses a
library to create a new document with the content received from the API.
Here's a step-by-step breakdown of what the script does:
1. Initialize Application Object: It starts by creating an instance of the current
application (`Application.currentApplication()`) and enables the use of
standard OS additions like displaying alerts or running shell scripts
(`app.includeStandardAdditions = true`).
2. Library Import: Imports a library named 'bebe' (`Library('bebe')`). This
library is used later in the script for creating a new document, indicating it
provides some document management functionality.
3. Prepare API Call: Sets a variable `prompt` with the text "make list of ways
to research javascript language". This is the data that will be sent to an
external service.
4. CURL Command Construction: Constructs a `curl` command to make a
POST request to
`https://generativelanguage.googleapis.com/v1beta/models/gemini-
pro:generateContent` with a JSON payload. This payload includes the
`prompt` variable content. The command is structured to send a JSON request
to an API (identified by a placeholder `API-KEY`).
5. Execute and Process API Response:
- Try Block: Executes the `curl` command using the
`app.doShellScript(command)` method, capturing the JSON response in
`jsonObj`.
- Parses the `jsonObj` string to an object (`obj`) using
`JSON.parse(jsonObj)`.
- Extracts a specific piece of text (`googleText`) from the parsed object,
which seems to be the main content returned by the API in response to the
prompt.
6. Error Handling: If there's an error in executing the `curl` command or
processing the JSON response, it catches the error and displays an alert with
the error message using `app.displayAlert`.
7. Use of 'bebe' Library: Creates a new document using the 'bebe' library
(`bebe.newBBDocWithText ('JSON',googleText)`), with the text extracted
from the API response.
This script is essentially automating the process of sending a prompt to an
external generative language model API, receiving a response, and then using
the response to create a new document via a custom library.
OceanofPDF.com

OceanofPDF.com
Add keywords to selected images in Photos
// JXA script to add keywords and optionally update the caption of selected
photos in the Photos app
// Initialize Photos application and include standard additions for dialogs and
alerts
const Photos = Application('Photos');
Photos.includeStandardAdditions = true;
Photos.activate(); // Bring Photos app to the foreground
// Retrieve the currently selected photos
const selection = Photos.selection();
// Check if at least one photo is selected
if (selection.length < 1) {
Photos.displayAlert("No Photos Selected", {
message: "A photo must be selected before running the script.",
as: "warning"
});
} else {
// Prompt user for keywords
const keywordsToAdd = userInput("Enter your keywords separated by
commas:", "keys").split(",");
// Loop through each selected photo
selection.forEach(photo => {
// Retrieve existing keywords and combine them with the new ones
const existingKeywords = photo.keywords();
const combinedKeywords =
keywordsToAdd.concat(existingKeywords);
// Update the photo's keywords
photo.keywords = combinedKeywords;
// Ask user if they want to also update the caption
const captionChoice = Photos.displayDialog('Keywords added. Do you
want to also update the caption?', {
buttons: ["Cancel", "Also Caption", "Keywords Only"],
defaultButton: "Keywords Only"
});
// Handle caption update based on user's choice
if (captionChoice.buttonReturned === "Also Caption") {
const newCaption = userInput("Enter your caption:", "cap");
photo.description = newCaption; // Update the photo's caption
}
});
}
// Function to display a dialog to the user and return their input
function userInput(prompt, type) {
const response = Photos.displayDialog(prompt, {
defaultAnswer: "",
buttons: ["OK"],
defaultButton: "OK"
});
return response.textReturned; // Return the user's input text
}
OceanofPDF.com

OceanofPDF.com
Reveal a File's Spotlight Information
// Selecting a file will reveal all the spotlight information that is not null and
puts in a TextEdit document
app = Application.currentApplication()
app.includeStandardAdditions = true
file = app.chooseFile({withPrompt:"Choose a File to Get kMDItem info"})
thePath = Path(file)
info = app.doShellScript("mdls '" + file + "'")
kMDinfo = ""
elements = info.split('\r')
elements.forEach(element => {
if(!element.includes('null')){
kMDinfo = kMDinfo + element + '\n'
}
})
textedit = Application("TextEdit")
document = textedit.make({new: "document"})
document.text = kMDinfo
document.text.color = [256, 40421, 398]
This script allows a user to select a file on their Mac, then retrieves and
displays all the available Spotlight metadata for that file, excluding any
metadata values that are null. Here's a step-by-step breakdown of what the
script does:
1. It initializes the current application with standard additions, enabling it to
use system dialogs and shell scripts.
2. The user is prompted to choose a file. The script then captures the path of
the selected file.
3. It executes a shell command (`mdls`) with the selected file's path to retrieve
its metadata. The `mdls` command is used to list the metadata attributes of a
file in macOS.
4. The script processes the output of the `mdls` command, splitting it by line
breaks (`\r`).
5. It then iterates through each line of the metadata information. If a line does
not include the string 'null', indicating that the metadata attribute has a value,
it's added to a string variable with a newline character for separation.
6. After filtering out the null values, the script creates a new document in
TextEdit.
7. It sets the text of the document to the filtered metadata information.
8. Lastly, it sets the color of the text in the document to a specific colorvalue.
In summary, the script is designed to provide a user-friendly way to view the
non-null metadata of any file on macOS using Spotlight's `mdls` command,
displaying the results in a new TextEdit document.
OceanofPDF.com

OceanofPDF.com
Convert Numbers Spreadsheet Data to JSON
Here's a step-by-step breakdown of what the script does:
Load the bebe Library: The script starts by loading the bebe library, which is
assumed to provide functionalities for handling files.
Access the Numbers Application and Active Table: The script accesses the
Numbers application and retrieves the first table in the active sheet of the first
document. This table is where the data to be converted to JSON is located.
Determine the Number of Rows and Columns: The script calculates the
number of rows and columns in the table to understand the range of data that
needs to be processed.
Retrieve Column Headings: The script iterates through the first row (assumed
to be the header row) of the table and collects the values of each cell. These
values are used as keys for the JSON objects.
Convert Table Rows to JSON Objects: For each row in the table, the script
creates a new object (rowObj). It then iterates through each cell in the row,
using the column headings as keys and the cell values as values. Each
completed rowObj represents a row in the table and is added to the sheetArray
Convert the Array to JSON: Once all rows have been processed, the
sheetArray is converted into a JSON string using JSON.stringify. This string
(jText) represents the entire table in JSON format.
Create a New Document with the JSON Content: Finally, the script uses the
bebe library to create a new document named "Json File" containing the JSON
string.
The end result is a JSON file that mirrors the structure of the original
spreadsheet in Numbers, with each row represented as an object in an array,
and each column heading used as the key for the corresponding values in the
objects.
bebe = Library("bebe")
numbersApp = Application('Numbers');
frontTable = numbersApp.documents[0].activeSheet().tables[0];
theRows = frontTable.rowCount();
theCols = frontTable.columnCount();
sheetArray = [];
// Get column headings
columnHeadings = [];
for (j = 0; j < theCols; j++) {
columnHeadings.push(frontTable.rows[0].cells[j].value());
}
// Convert rows to objects using column headings as keys
for (i = 0; i < theRows; i++) {
rowObj = {};
for (j = 0; j < theCols; j++) {
key = columnHeadings[j];
value = frontTable.rows[i].cells[j].value();
rowObj[key] = value;
}
sheetArray.push(rowObj);
}
jText = JSON.stringify(sheetArray, null, 2);
// Assuming bebe is a library for handling files
bebe.newBBDocWithText("Json File", jText);
OceanofPDF.com
Use a Custom GPT called "JXA Helper"
Web site: https://chat.openai.com/g/g-yKtWFdI87-jxa-helper
Prompt: Write a JXA script that retrieves cells A1-H16 from numbers and
create a series of Keynote slides from the rows and then writes a presentation
script.
Creating a JXA (JavaScript for Automation) script to retrieve data from a
Numbers spreadsheet and then use that data to create Keynote slides, followed
by drafting a presentation script, involves a few steps. Below is a basic outline
of how you can approach this task. Keep in mind that this script assumes you
have a Numbers document with data in cells A1-H16 and Keynote is used to
create slides based on this data.
// Define a function to retrieve data from Numbers
function getDataFromNumbers() {
const Numbers = Application('Numbers');
Numbers.activate();
const doc = Numbers.documents[0]; // Assumes the document is open and
is the first one
const sheet = doc.sheets[0]; // Assumes data is in the first sheet
const table = sheet.tables[0]; // Assumes data is in the first table
// Range A1-H16
const range = table.ranges['A1:H16'];
const data = range.cells.value();
return data;
}
// Define a function to create Keynote slides
function createKeynoteSlides(data) {
const Keynote = Application('Keynote');
Keynote.activate();
const doc = Keynote.documents[0]; // Assumes a document is open and is
the first one
data.forEach((row, index) => {
const slide = Keynote.Slide({body: row.join(' ')}); // Combine row data
into a string for the slide body
doc.slides.push(slide);
if (index === 0) {
slide.baseSlide = Keynote.themes[0].masterSlides[0]; // Use the
first theme and master slide for the first slide
}
});
}
// Define a function to generate a simple presentation script based on the data
function generatePresentationScript(data) {
let script = "Presentation Script:\n\n";
data.forEach((row, index) => {
script += `Slide ${index + 1}: Discuss ${row.join(', ')}. \n\n`;
});
return script;
}
// Main function to orchestrate the workflow
function main() {
const data = getDataFromNumbers();
createKeynoteSlides(data);
const presentationScript = generatePresentationScript(data);
console.log(presentationScript);
}
// Execute the main function
main();
This script does the following:
1. Retrieves data from a specified range (A1-H16) in the first table of the first
sheet of the first open Numbers document.
2. Creates a new slide in an already open Keynote presentation for each row of
data. Each slide's content is based on the row's data.
3. Generates a simple presentation script that outlines what to discuss on each
slide, based on the data.
Please make sure you have the necessary Numbers document and Keynote
presentation open before running this script. Adjustments may be required
based on your specific Numbers and Keynote document setups (e.g.,
document indices, sheet names, table names, etc.).
Note: The script simplifies several steps for clarity and brevity. Depending on
your specific needs, you might need to adjust the script, especially to handle
different data structures or to customize the slide creation further in Keynote.

OceanofPDF.com
Calling to Google's API from Filemaker for
Research
app = Application.currentApplication()
app.includeStandardAdditions = true
fm = Application("Filemaker Pro")
se = Application("System Events")
db = fm.layouts['Picture List']
len = db.records().length
for(i=0;i<len;i++){
rawText = db.records[i].cells[1]()
rawText = rawText.replaceAll("'", "")
rawText = rawText.replaceAll('\n','')
rawText = rawText.replaceAll('"','')
rawText = rawText.replaceAll(':','')
prompt = "Write a factual 250-word profile of the following " + rawText
command = `curl -H 'Content-Type: application/json' -d '{"contents":[{"parts"
[{"text":"${prompt}"}]}]}' -X POST
https://generativelanguage.googleapis.com/v1beta/models/gemini-
pro:generateContent?key=API-KEY`
try {
jsonObj = app.doShellScript("/usr/local/bin/bash; " + command)
obj = JSON.parse(jsonObj)
googleText = obj.candidates[0].content.parts[0].text
} catch (error) {
response = app.displayAlert(error.message, {
message: error.message,
as: 'critical'
})
}
delay(5)
db.records[i].cells[2] = googleText
}
This script automates the process of fetching artist names from a FileMaker
Pro database, generating research information using an external API, and then
updating the database with the generated content. Here's a step-by-step guide
to what it does:
1. Initialize the Script Environment: It starts by setting up the current
application (likely an automation script environment) to include standard
additions, which allow it to use additional features like displaying alerts or
executing shell scripts.
2. Connect to Applications: It establishes connections with FileMaker Pro
(referred to as `fm`) and System Events (`se`). These connections enable the
script to interact with a FileMaker Pro database and perform system-level
actions, respectively.
3. Access the Database Layout: The script accesses a specific layout named
'Picture List' in the FileMaker Pro database. This layout is assumed to contain
records of the project.
4. Determine the Number of Records: It calculates the total number of records
(artists) present in the 'Picture List' layout.
5. Loop Through Each Record:
- For each record, it retrieves the artist's name stored in the first cell
(presumably, cells are columns in the database layout) of the current record.
- Cleans up the artist's name by removing specific characters (`'`, `\n`, `"`,
`:`) that might cause issues in the subsequent API request.
6. Prepare and Send API Request:
- Constructs a prompt by appending the cleaned artist's name to a
predefined request text ("Write a factual 250-word profile of the following
blues artist: ").
- Constructs a command to send this prompt to an external API (identified
by its URL and including an API key in the request) using `curl`. The API is
expected to generate content based on the prompt.
- Executes the command in a bash shell and captures the JSON response.
7. Handle the API Response:
- Parses the JSON response to extract the generated content.
- In case of an error (e.g., network issue, API error), displays an alert with
the error message.
8. Update the Database Record:
- Updates the second cell of the current record with the generated content
- Includes a delay of 5 seconds before proceeding to update the next record,
likely to avoid overloading the API or running into rate limits.
9. Repeat: This process is repeated for each record in the 'Picture List' layout,
effectively populating the database with generated data.
This script automates the generation and updating of artist profiles in a
database, showcasing the integration of database manipulation, external API
requests, and error handling in an automation script.

OceanofPDF.com
Transfer Page from Safari to Google Chrome
Safari = Application('Safari')
Chrome = Application('Google Chrome')
myUrl = Safari.windows[0].currentTab.url()
Chrome.activate()
newWindow = Chrome.Window().make();
newWindow.tabs.push(Chrome.Tab({url: myUrl }));
This script is designed to interact with both Safari and Google Chrome web
browsers on a Mac using JavaScript for Automation (JXA). Here's a step-by-
step breakdown of what it does:
1. Initialize Application Objects for Safari and Chrome: The script starts by
creating application objects for both Safari (`Application('Safari')`) and
Google Chrome (`Application('Google Chrome')`). These objects allow the
script to control and interact with each browser.
2. Retrieve the Current URL from Safari: It then retrieves the URL of the
current tab in the frontmost window of Safari using
`Safari.windows[0].currentTab.url()`. This assumes Safari is running and has
at least one window open with an active tab.
3. Activate Google Chrome: The script activates Google Chrome, bringing it
to the forefront, making it the active application on the screen.
4. Create a New Window in Chrome: A new window is created in Google
Chrome using `Chrome.Window().make();`. This command effectively opens
a new Chrome window.
5. Open the URL from Safari in Chrome: Finally, the script creates a new tab
in the newly created Chrome window and sets its URL to the one previously
obtained from Safari's current tab. This is done through
`newWindow.tabs.push(Chrome.Tab({url: myUrl }));`.
This script transfers the URL of the currently active tab in Safari to a new tab
in a new window of Google Chrome, effectively duplicating the tab across
browsers. This could be particularly useful for users who work with multiple
browsers and need to move sessions or specific pages from one to the other.
This is handy for those times when you use Safari most of the time, but
occasionally use Google Chrome to access sites separately from the whole
Safari eco-system. I have set a scripts that will send a URL to-and-from
Microsoft Edge, for those times when I am using that browser for one reason
or another.
OceanofPDF.com

OceanofPDF.com
Extract Shorter AI-Picked Playlist from Longer
Apple Music allows the creation of mega-playlists that can contain thousands
of songs. When one has a playlist of so many thousands, it is astonishing how
many unique playlists that of a certain length that can be generated. The great
part is to put the mega-playlist on shuffle and get a unique list each time. But
the larger script can serve as a pool of song. This script uses AI to pick the
songs on a predetermined criteria.
This script is a sophisticated automation tool designed to streamline playlist
management within the Music application on macOS. Leveraging the power
of JavaScript for Automation (JXA), the script integrates several macOS
applications and functionalities to enhance user productivity by automating
repetitive tasks associated with playlist curation.
The script begins by initializing key macOS applications: Music, ChatGPT,
System Events, and the current application. This setup ensures that the script
can interact seamlessly with these applications, utilizing their full range of
capabilities. The inclusion of standard additions further extends the script's
functionality, allowing for more complex operations.
One of the primary features of the script is its ability to verify the existence of
a specified playlist within the Music app. This initial check ensures that the
script operates on the correct data set, preventing errors and enhancing
reliability. If the specified playlist is not found, the script logs an appropriate
error message and halts further execution, providing clear feedback to the
user.
Once the playlist is confirmed, the script proceeds to extract track information
It systematically collects the names of tracks from the playlist and formats this
data into a CSV string. This step prepares the track information for further
processing, ensuring that the data is structured in a way that facilitates easy
manipulation and analysis.
The script then constructs a detailed prompt for ChatGPT, asking it to select
the 25 most interesting songs from the provided list. This prompt is copied to
the clipboard, and ChatGPT is activated to process the request. Through a
series of simulated keystrokes, the script interacts with ChatGPT, pasting the
prompt, sending the request, and retrieving the response. This innovative use
of ChatGPT highlights the script's ability to incorporate advanced AI tools to
enhance decision-making and streamline workflows.
After receiving the response from ChatGPT, the script processes the list of
selected tracks, refining the data to remove any unnecessary elements. It logs
the final list of tracks, providing a transparent view of the selected items.
Subsequently, the script creates a new playlist in the Music app, aptly named
to reflect its origin, and duplicates the selected tracks into this new playlist.
This feature showcases the script's ability to not only curate but also organize
and manage music collections effectively.
In essence, this script exemplifies the powerful synergy between JavaScript
for Automation and macOS applications. By automating the intricate process
of playlist management, it saves time and effort, allowing users to focus on
enjoying their music rather than getting bogged down by administrative tasks.
The integration of ChatGPT further enhances its utility, demonstrating a
forward-thinking approach to leveraging AI for practical, everyday
applications.
How to Create a Web App on macOS for the ChatGPT Website for Use in
the Script
Creating a web app from a webpage on macOS allows you to use the webpage
as an independent application, enhancing your workflow and keeping your
browsing activities separate. Here’s how you can create a web app for the
ChatGPT website, which will be used in the automation script.
music = Application('Music')
gpt = Application('ChatGPT')
gpt.includeStandardAdditions = true
se = Application('System Events')
app = Application.currentApplication()
app.includeStandardAdditions = true
prePrompt = ""
theLists = ["Ultimate Blues Collection Collection"]
existingPlaylist = music.playlists.whose({ name: theLists[0] })[0]
// Ensure the playlist exists
if (!existingPlaylist) {
console.log("Playlist not found: " + existingPlaylistName);
throw new Error("Playlist not found: " + existingPlaylistName);
}
for(i=0;i<theLists.length;i++){
currentPlaylist = theLists[i]
trackNames = []
csvText = ''
// Data
theTracks = music.userPlaylists[currentPlaylist].tracks()
len = theTracks.length
theTracks.forEach(getName)
tlen = trackNames.length
trackNames.forEach(makeCsv)
}
prePrompt = "Pick out the 25 most interesting sogs from the provided list and
produce a list in the format of a return-delimited list as code using \r: "
prompt = prePrompt + csvText
app.setTheClipboardTo(prompt)
gpt.activate()
// se.keystroke("o", { using: ["shift down","command down"] })
delay(2)
se.keyCode(53, { using: ["shift down"] })
delay(2)
se.keystroke("v", { using: ["command down"] })
se.keyCode(76)
delay(3)
gpt.displayDialog('Send to IDE', {buttons: ["Cancel", "Proceed"],
defaultButton: "Proceed", withTitle: "Playlist Process", withIcon: "note",
givingUpAfter: 60 })
delay(2)
se.keystroke("c", { using: ["shift down","command down"] })
content = app.theClipboard()
finalTracks = content.split("\r")
// Remove the first item: the apostrophes and plain text
finalTracks.shift()
// Remove the last item: the apostrophes
finalTracks.pop();
console.log(finalTracks.toString())
newPlaylist = music.Playlist().make()
newPlaylist.name = "Extracted from " + theLists[0]
newPlaylist.description = "This playlist is extracted from " + theLists[0]
delay(2)
for(j=0;j<finalTracks.length;j++){
songName = finalTracks[j]
console.log(songName + " ")
song = existingPlaylist.tracks.whose({ name: songName })[0]
delay(2)
music.duplicate(song, { to: newPlaylist })
}
function getName(tr){
trackNames.push(tr.name() + '\n')
}
function makeCsv(item){
csvText += item
}
Step-by-Step Description of the Script
1. Initialize Applications:
- The script starts by initializing the Music application, ChatGPT
application, System Events, and the current application. It also enables
standard additions for these applications to extend their capabilities.
2. Verify Playlist Existence:
- The script checks if a specified playlist exists in the Music app. If the
playlist is not found, it logs an error message and stops further execution.
3. Extract Track Information:
- The script retrieves the names of all tracks from the specified playlist. It
stores these track names in a list and formats them into a CSV string to
prepare the data for further processing.
4. Prepare ChatGPT Prompt:
- A detailed prompt is created, asking ChatGPT to select the 25 most
interesting songs from the provided list. This prompt is copied to the
clipboard.
5. Activate ChatGPT:
- The script activates ChatGPT and simulates keystrokes to paste the
prompt, send the request, and retrieve the response. This interaction with
ChatGPT is designed to leverage its AI capabilities for refining the track
selection.
6. Process ChatGPT Response:
- The script processes the response from ChatGPT, extracting the list of
selected tracks. It cleans up the data by removing any unnecessary elements.
7. Create New Playlist:
- A new playlist is created in the Music app. This playlist is named to reflect
its origin from the specified playlist.
8. Duplicate Selected Tracks:
- The script duplicates the tracks selected by ChatGPT into the newly
created playlist. It iterates through the list of selected tracks, finds each track
in the existing playlist, and copies it to the new playlist.
9. Log and Display Information:
- Throughout the process, the script logs relevant information and displays
dialogs as needed to keep the user informed of its progress and any actions
that require user confirmation.
By following these steps, the script automates the task of curating and
organizing music playlists, using the combined power of macOS automation
tools and AI-driven selection through ChatGPT.
OceanofPDF.com

OceanofPDF.com
Customer Support
I wanted to make a JXA (JavaScript for Automation) script for find and
replace in BBEdit. I have a decent amount of success at figuring the right
translation and the more you do it, the better you get. But somehow, I could
not get the find method or the replace method to work in a JXA script. No
matter how I tweaked the syntax, I only was able to get errors and unusual
results. As is usually the case, there are other possible ways to do the task.
First, I could just stick with the AppleScripts I have that do the job very well.
Here is one that cleans up some text scraped from a web page:
tell application "BBEdit"
replace "\\." using "" searching in selection of text 1 of text document 1
options {search mode:grep, starting at top:true}
replace "\\-" using "" searching in selection of text 1 of text document 1
options {search mode:grep, starting at top:true}
replace "'" using "" searching in selection of text 1 of text document 1
options {search mode:grep, starting at top:true}
replace "é" using "e" searching in selection of text 1 of text document 1
options {search mode:grep, starting at top:true}
replace "ú" using "u" searching in selection of text 1 of text document 1
options {starting at top:true, case sensitive:true}
replace "\\n" using "," searching in selection of text 1 of text document 1
options {starting at top:true, case sensitive:true}
replace " " using "" searching in selection of text 1 of text document 1
options {starting at top:true, case sensitive:true}
end tell
Second, I can call this script from within a JXA script using "doShellScript" to
do the replace:
app = Application.currentApplication();
app.includeStandardAdditions = true;
result = app.doShellScript("osascript /users/scripts/removecaret.scpt")
Third I could use a Javascript function to replace in the text. Which would
work, but BBEdit has always been my grep go to:
app = Application.currentApplication();
app.includeStandardAdditions = true;
se = Application('System Events')
bb = Application('BBEdit')
bodyText = bb.documents[0].text()
changedText = grepFindAndReplace(bodyText, 'Read.+\n', '')
function grepFindAndReplace(inputString, searchPattern, replaceWith) {
const regex = new RegExp(searchPattern, 'g');
const outputString = inputString.replace(regex, replaceWith);
return outputString;
}
bb.documents[0].text = changedText
My answer is number three. Since BBEdit Applescript recognizes the various
groups of text it works on as structured text of the types that Javascript can
handle very well, reading, processing, and writing to multiple possible
locations.
Regular expressions are patterns used to match character combinations in
strings. In JavaScript, regular expressions are also objects. These patterns are
used with the exec() and test() methods of RegExp, and with the match(),
matchAll(), replace(), replaceAll(), search(), and split() methods of String.
OceanofPDF.com
Shortcuts
OceanofPDF.com
Generate QR Code
1. Receive more input from Share Sheet: This action is set to receive input
from the share sheet. If no input is provided, it will continue to the next action
This is likely the initial point where the shortcut receives the URL to convert
into a QR code.
2. Get URLs from Shortcut Input: This action extracts URLs from the input
received by the shortcut. If a user selects text or a file that contains a URL and
shares it to the shortcut, this action ensures that only the URL is taken as input
for the next step.

3. Generate QR code from URLs: This action takes the URL extracted from
the previous step and generates a QR code image from it. This is the core
functionality of the shortcut.
4. Save QR Code to Recents: Finally, this action saves the generated QR code
image to the user's 'Recents' album in the Photos app.
Description of the Shortcut:
This shortcut is designed to take a URL from the user, via the share sheet, and
convert it into a QR code. This QR code is then saved to the 'Recents' album
in the Photos app for easy access. This shortcut could be useful for quickly
sharing URLs in a visual format that can be scanned by smartphones or other
devices with a camera and QR code scanning capability. It's a simple and
efficient way to generate QR codes without needing a dedicated app or
website.
OceanofPDF.com
Select Photos to Extract Text into a Note

1. Ask for Text: The shortcut prompts the user to enter text to be used as the
name for a new note. The prompt given to the user is "Make a name for note".
2. Create note: The shortcut creates a new note with the provided input (the
name entered in the previous step) in a folder or account named "Biz".
3. Select photos: The user is prompted to select photos from their library.
4. Extract text from Photos: The shortcut extracts text from the selected photos
using OCR (Optical Character Recognition) technology.
5. Append: The extracted text is then appended to the note created earlier.
Description of the Shortcut:
This shortcut is designed to extract text from one or more selected photos and
save that text in a note. The user first names the note, then selects the photos.
The text is recognized from these images and compiled into the newly created
note within the "Biz" folder or account. This is useful for quickly capturing
and organizing text from physical documents or images into a digital format
for easy access and reference later on.
OceanofPDF.com
Creating a quiz with Changeable Questions
The images outline a macOS Shortcut named "Monarchy Quiz". This shortcut
appears to function as a simple quiz application that asks questions about
monarchs, specifically Edward I, and then displays the answers.


Here's a step-by-step description of how the shortcut works:
1. The shortcut contains a block of text formatted as a JSON array, with each
object in the array containing a category (which is "Edward I"), a subcategory
(such as "Biography" or "Reign"), a question, and an answer.
2. The shortcut then extracts a dictionary from this JSON-formatted text.
3. It enters a repeat loop that will iterate over each item in the dictionary.
4. For each item (each question and answer pair), it retrieves the value for the
key 'category' within the current repeat item (although this step seems to have
no further use in the remaining actions).
5. Then, it retrieves the value for the key 'question' within the repeat item.
6. The question is then displayed to the user via a 'Show alert' action.
7. Afterward, it retrieves the value for the key 'answer' within the repeat item.
8. This answer is also displayed to the user via a 'Show alert' action.
9. The loop ends after all items in the dictionary have been processed.
To use the shortcut:
- Run the "Monarchy Quiz" shortcut.
- Read the question displayed in the alert and think of your answer.
- Dismiss the question alert to see the correct answer in the next alert.
- Repeat this process for each question in the quiz until all have been
answered.
This shortcut is a straightforward way to present a series of questions and
answers to the user, likely for educational or entertainment purposes.
OceanofPDF.com
Create a Playlist Image with AI
A recent update to Apple Music contained an odd surprise. In the past, when a
new playlist as created, the cover art for the first four albums is used to create
the "cover" for the playlist. One could control the cover art used by putting the
four songs that featured the desired cover art at the first of the list. But the
update completely took this away and offered some particularly ugly pre-made
images as choices, except for the option of adding an image from a file or the
phot library. To do this after making a new playlist was cumbersome, so I used
shortcuts to streamline it in two ways. First using the website OpenArt.ai to
create an image
to use:
OceanofPDF.com

The images show parts of a macOS Shortcut named "Use AI To Get Playlist
Art". This shortcut is designed to help you create artwork for a music playlist
with a specific style in mind. Here's how it works:
1. The shortcut begins with an action to continue receiving input if there is no
direct input from the Share Sheet.
2. A Dictionary action is used to define a list of styles that can be chosen to
determine the type of artwork you want to create. The options provided
include "Comic" for Comic Book style, "Art Nouveau," "Gothic," "Picasso"
(likely substituting for a more abstract style), "California," "Sixties," and
"Movie". You can also add more styles to this dictionary.
3. The 'Choose from Dictionary' action allows you to pick one of these styles.
4. Then, the shortcut asks for text input with a prompt, presumably to receive
more specific details or preferences for the artwork.
5. The selected item from the dictionary (the chosen style) and the provided
input text are combined into a single text block.
6. This text block is copied to the clipboard.
7. A URL is created, pointing to "https://www.openart.ai/create".
8. The shortcut opens this URL, where the combined text block, which
contains the style and any additional input, could be used to generate playlist
art using AI.
To use the shortcut:
- Run the shortcut and choose a style from the predefined list.
- Input any additional details when prompted.
-
- The shortcut will copy the combined text to your clipboard and navigate to
the openart.ai website, where you can use the copied text to instruct an AI to
generate playlist artwork in the chosen style.
OceanofPDF.com
Find a Playlist Image with Google
Second, to use Google to search for an image suitable for use as a playlist
cover. At the playlist, touch the three dots to get the share sheet. Select the
name of the Shortcut. If a song is playing, the Shortcut will immediately use
the Album name, Song Title, and Artist of the Current Song as search terms,
otherwise it will present an audio input to give the search terms by voice:


OceanofPDF.com

OceanofPDF.co
m