Appearance
JavaScript Plugins & Extensibility
Kalc can be extended with custom mathematical functions, specialized physical units, and formula templates written in JavaScript and bundled into .kalcplugin packages.
Extensions run inside Apple's native JavaScriptCore framework (JSVirtualMachine, JSContext), isolated within a secure sandbox with a 500ms execution timeout.
The .kalcplugin Bundle Structure
A .kalcplugin package is a directory (or compressed .zip archive) ending with .kalcplugin:
Finance.kalcplugin/
├── manifest.json # Plugin metadata, signatures, AI definitions, and templates
├── plugin.js # JavaScriptCore implementation using the kalc bridge
└── README.md # Documentation and usage instructions1. The manifest.json Schema
The manifest declares metadata, function signatures, and formula templates. Kalc parses this file to automatically populate autocomplete suggestions, documentation hover popovers, the formula template browser, and natural language matching in the Formula Syntax Assistant:
json
{
"id": "com.kalc.finance",
"name": "Finance & Wealth Extensions",
"version": "1.0.0",
"author": "Kalc Team",
"description": "Retirement future value, compound interest, and loan calculators.",
"functions": [
{
"name": "futureValue",
"signature": "futureValue(principal, rate, monthly, years)",
"detail": "Calculates future investment value under monthly contributions.",
"parameters": [
{ "name": "principal", "description": "Initial starting balance" },
{ "name": "rate", "description": "Annual return percentage (e.g. 7% or 0.07)" },
{ "name": "monthly", "description": "Monthly recurring contribution" },
{ "name": "years", "description": "Investment horizon in years" }
],
"examples": [
"futureValue($10k, 7%, 500, 20)",
"futureValue($50k, 8.5%, $1200, 25)"
],
"ai": {
"label": "Investment Future Value",
"category": "finance",
"aliases": ["future value", "investment growth", "portfolio projection"],
"examples": [
"future value of $10k at 7% with $500 monthly for 20 years",
"grow $50k at 8% adding 1000 a month for 25 years"
],
"parameters": [
{ "name": "principal", "kind": "currency", "required": true, "description": "Initial balance" },
{ "name": "rate", "kind": "percentage", "required": true, "description": "Annual nominal return rate" },
{ "name": "monthly", "kind": "currency", "required": true, "description": "Monthly recurring deposit" },
{ "name": "years", "kind": "duration", "required": true, "description": "Horizon in years" }
]
}
}
],
"templates": [
{
"id": "mortgage_amortization",
"title": "Mortgage Amortization",
"category": "Finance",
"description": "Calculates monthly mortgage payments and total interest.",
"template": "loan = $500,000\nrate = 4.25%\nyears = 30\npmt(loan, rate, years)\ntotal repayment on loan for years at rate\n"
}
]
}The PluginAIMetadata Specification
To expose a plugin function to the Formula Syntax Assistant, include an optional "ai" block:
label(String): Concise human-readable title shown in the gutter suggestion pill.category(String): Domain grouping (e.g."finance","engineering","fitness","tax").aliases([String]): Natural language trigger phrases, synonyms, and search keywords.examples([String]): Conversational sample queries.notFor([String], Optional): Negative intent triggers to prevent false matches against adjacent concepts.parameters([Parameter]): Formal typed parameter descriptors (kindmust be one of:number,integer,percentage,currency,unitValue,duration,date,string,boolean).
Dynamic Catalog Revision Hash
When plugins are loaded or toggled, CapabilityCatalog generates a deterministic FNV-1a 64-bit revision hash ("fnv1a:<hex>"). This revision is transmitted to the edge service, ensuring proposals match the client's exact local plugin configuration.
2. Safe Preview Gate (isDeferredExecution)
Kalc enforces strict execution security for plugin suggestions:
- Zero Background JS Execution: During editor typing,
FormulaPreviewGateverifies that AST expressions are syntactically sound without invoking third-party JavaScript functions. - If a plugin call is detected,
isDeferredExecution = trueis flagged:textEditor (Line 3): monthly payment on 500k at 6% for 30 years Result Gutter: [Tab] mortgagePayment($500k, 6%, 30) - The dry-run preview is deferred until the user explicitly presses Tab or Return, preventing unvetted scripts from running in the background while typing.
3. The kalc JavaScript Bridge API
Inside plugin.js, the global kalc object exposes methods to register functions, custom variables, and units:
kalc.addFunction(name, callback)
Registers a custom JavaScript function callable directly from expressions:
javascript
// plugin.js
kalc.addFunction("doubleValue", function(val) {
return val * 2;
});
kalc.addFunction("futureValue", function(principal, rate, monthly, years) {
var r = (rate > 1) ? (rate / 100) : rate;
var monthlyRate = r / 12;
var months = years * 12;
// FV = P * (1 + r)^n + PMT * [((1 + r)^n - 1) / r]
var fvPrincipal = principal * Math.pow(1 + monthlyRate, months);
var fvAnnuity = monthly * ((Math.pow(1 + monthlyRate, months) - 1) / monthlyRate);
return fvPrincipal + fvAnnuity;
});kalc.setVariable(name, value)
Exports a global custom variable accessible across all document buffers:
javascript
kalc.setVariable("standardVat", { double: 0.077, unitId: "percent" });
kalc.setVariable("goldOunceUsd", { double: 2650.00, unitId: "USD" });kalc.addUnit(definition)
Registers a new physical or conceptual unit into UnitCatalog:
javascript
kalc.addUnit({
id: "parsec",
names: ["parsec", "pc"],
baseUnitId: "m",
ratio: 3.085677581491367e16
});4. Sandbox Security Model
To protect user privacy and system integrity, KalcPlugins enforces strict security boundaries:
- No Filesystem Access: Extensions cannot read, write, or enumerate files on disk.
- No Process Execution: Plugins cannot invoke shell binaries (
bash,zsh), spawn child processes, or run command-line tools. - No Outbound Networking: The
JSContextdoes not exposefetch,XMLHttpRequest, or raw TCP/UDP socket APIs. - 500ms Watchdog Protection: Infinite loops or blocking scripts are terminated automatically after 500ms without freezing the host application.
5. Bundled First-Party Reference Plugins
Kalc includes 8 pre-installed calculation packs located in apps/macos/Extensions/:
1. Finance.kalcplugin
futureValue(principal, rate, monthly, years): Investment compounding with monthly additions.totalInvested(principal, monthly, years): Total cumulative contributions.interestEarned(principal, rate, monthly, years): Pure investment returns.compound(principal, rate, years): Single lump-sum compound interest.salesTax(amount): Standard state/provincial sales tax calculation.
2. AustraliaTax.kalcplugin & CanadaTax.kalcplugin
australiaTax(income): Computes ATO individual income tax brackets, Low Income Tax Offset (LITO), and 2% Medicare levy.australiaNetIncome(income): Net annual take-home salary after Australian statutory withholding.canadaTax(income): Evaluates Canadian federal tax brackets and Ontario provincial baseline rates.canadaNetIncome(income): Net annual take-home salary after Canadian statutory withholding.
3. PersonalWealth.kalcplugin
mortgagePayment(principal, rate, years): Fixed monthly mortgage amortization payment.mortgageInterest(principal, rate, years): Cumulative mortgage interest paid over the loan term.mortgageTotalCost(principal, rate, years): Total mortgage repayment (principal + cumulative interest).billSplit(subtotal, tipPercent, people): Per-person dining cost with discretionary tip.billTipAmount(subtotal, tipPercent): Tip dollar amount on a restaurant subtotal.billTotal(subtotal, tipPercent): Total dining bill inclusive of tip.dayRateToAnnual(dayRate, workingDays): Consultant day rate annualization.annualToHourly(salary, hoursPerWeek, weeksPerYear): Converts annual base salary to an hourly wage.hourlyToAnnual(hourlyRate, hoursPerWeek, weeksPerYear): Converts hourly rate to full-time annual salary.cagr(startValue, endValue, years): Compound Annual Growth Rate over an arbitrary horizon.roi(startValue, endValue): Return on Investment percentage.
4. CommercePricing.kalcplugin
priceFromMargin(cost, margin): Selling price required to achieve a target gross margin percentage.priceFromMarkup(cost, markup): Selling price after applying a markup percentage to cost.profitFromMargin(cost, margin): Dollar profit earned per unit sold at target margin.profitFromMarkup(cost, markup): Dollar profit earned per unit sold at target markup.netFromGross(grossPrice, taxRate): Net price before VAT/sales tax extracted from gross receipt.vatFromGross(grossPrice, taxRate): Tax portion extracted from a gross receipt.grossFromNet(netPrice, taxRate): Gross selling price after adding sales tax.customerLifespan(churnRate): Average customer lifetime in months ($\frac{1}{\text{churn}}$).ltv(arpu, churnRate, grossMargin): SaaS Customer Lifetime Value.cacRatio(ltv, cac): LTV to CAC payback sustainability ratio.
5. SoftwareEngineering.kalcplugin
transferSeconds(capacityGB, speedMbps): Network download duration in seconds.transferMinutes(capacityGB, speedMbps): Network download duration in minutes.transferHours(capacityGB, speedMbps): Network download duration in hours.ec2Monthly(instances, hourlyRate): Monthly cloud compute expense (730 hours/month).s3Monthly(storageGB, pricePerGB): Monthly cloud object storage cost.cloudMonthly(instances, hourlyRate, storageGB, pricePerGB): Combined compute and storage cost.scaleHeight(origWidth, origHeight, targetWidth): Proportionally scales image or screen height.scaleWidth(origWidth, origHeight, targetHeight): Proportionally scales image or screen width.aspectRatioWidth(width, height)&aspectRatioHeight(width, height): Simplified aspect ratio fractions.clampSlopeVw(minSize, minViewport, maxSize, maxViewport): Fluid typography CSSclamp()slope in viewport units.clampInterceptRem(minSize, minViewport, maxSize, maxViewport): Fluid typography CSSclamp()rem intercept.clampMinRem(minSize)&clampMaxRem(maxSize): Typography boundary rem tokens.
6. ProjectScheduling.kalcplugin
workingDaysCount(startMonth, startDay, endMonth, endDay, year): Net business days excluding weekends.workHoursCount(workDays, hoursPerDay): Total working billable hours.addWorkDays(startMonth, startDay, daysToAdd, year): Projects calendar delivery date skipping weekends.tzOffsetHours(baseHour, fromUtcOffset, toUtcOffset): Converts local time across arbitrary UTC offsets.elapsedHours(startHour, startMinute, endHour, endMinute): Decimal elapsed duration between two clock times.timesheetBill(hours, hourlyRate): Gross billable invoice total from logged timesheet hours.combinedTimesheetHours(h1, m1, h2, m2, h3, m3, h4, m4): Sums 4 multi-segment clock shifts.
7. DailyLifeFitness.kalcplugin
tripFuelVolume(distanceKm, consumptionLPer100Km): Total liters of fuel required for a driving distance.tripFuelCost(distanceKm, consumptionLPer100Km, pricePerLiter): Estimated fuel expenditure for a road trip.mpgToLPer100Km(mpg)&lPer100KmToMpg(lPer100Km): US MPG and European L/100km fuel economy conversions.runningPaceMinutes(distanceKm, timeMinutes): Running pace in minutes per kilometer.runningSpeedKmh(distanceKm, timeMinutes): Average running speed in km/h.scaleIngredient(fromServings, toServings, amount): Scales recipe ingredient quantities across serving yields.caloricDeficitKg(dailyDeficitKcal, weeks): Expected weight loss in kilograms over a diet period.caloricDeficitLbs(dailyDeficitKcal, weeks): Expected weight loss in pounds over a diet period.tcToFrames(hours, minutes, seconds, frames, fps): Converts SMPTE timecode to exact cumulative video frames.bakersIngredientWeight(flourWeight, bakersPercentage): Professional culinary baker's percentage ingredient weight.
6. Installing & Managing Plugins
- Open Preferences (
⌘ ,) and navigate to the Plugins tab. - Drag and drop any
.kalcpluginfolder or archive onto the window to install it. - Alternatively, click "Open Plugins Directory" to view installed bundles in:bash
~/Library/Application Support/Kalc/extensions/ - Use the toggle switches in preferences to enable or disable plugins dynamically without restarting the application.
