Compare commits

..

15 Commits

Author SHA1 Message Date
Oleksandr Kozachuk bc39eb3169 Simplify label. 2025-06-19 23:57:18 +02:00
Kiyomichi Kosaka ebbfc215b1 Merge pull request #55 from ok2/codex/find-issue-with-analog-clock-widget
Fix macOS widget resource bundling
2025-06-16 15:18:56 +02:00
Kiyomichi Kosaka 65e8457359 Add widget resource files 2025-06-16 15:18:43 +02:00
Kiyomichi Kosaka f4d4f518c6 Merge pull request #54 from ok2/codex/fix-widget-display-issue-on-macos
Fix macOS widget docs
2025-06-16 14:45:43 +02:00
Kiyomichi Kosaka 1d887cf2ca Update macOS widget instructions 2025-06-16 14:45:32 +02:00
Oleksandr Kozachuk 80e58578fc Fix widget. 2025-06-16 14:42:03 +02:00
Kiyomichi Kosaka 9256d5ecb7 Merge pull request #53 from ok2/codex/fix-bugs-in-cobie-analog-clock-widget
Fix WKWebView setup in widget
2025-06-16 14:39:09 +02:00
Kiyomichi Kosaka 4b8b29a657 Fix web view compilation issues 2025-06-16 14:38:54 +02:00
Kiyomichi Kosaka b749cfe5f4 Merge pull request #52 from ok2/codex/implement-cobie-analog-clock-widget
Implement CoBiE analog clock widget
2025-06-16 14:32:02 +02:00
Kiyomichi Kosaka 342e5c9c72 Add HTML-based analog clock widget 2025-06-16 14:31:47 +02:00
Oleksandr Kozachuk a486b97a2a Add empty Xcode project (remove old unsupported widget) 2025-06-16 14:25:00 +02:00
Kiyomichi Kosaka 81c93710ed Merge pull request #51 from ok2/codex/fix-widget-install-issue-on-mac-os-x
Fix macOS widget packaging
2025-06-16 13:00:58 +02:00
Kiyomichi Kosaka db3b93fc1b Fix macOS widget packaging 2025-06-16 12:58:21 +02:00
Kiyomichi Kosaka 9031736f7a Merge pull request #50 from ok2/codex/prevent-clock-size-change-on-font-scaling
Fix analog clock scaling
2025-06-15 15:23:19 +02:00
Kiyomichi Kosaka a6375d4148 Fix analog clock marker scaling 2025-06-15 15:23:09 +02:00
33 changed files with 2443 additions and 78 deletions
Vendored
BIN
View File
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
//
// AppIntent.swift
// CoBiE Analog Clock
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import WidgetKit
import AppIntents
struct ConfigurationAppIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource { "Configuration" }
static var description: IntentDescription { "This is an example widget." }
// An example configurable parameter.
@Parameter(title: "Favorite Emoji", default: "😃")
var favoriteEmoji: String
}
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,94 @@
//
// CoBiE_Analog_Clock.swift
// CoBiE Analog Clock
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import WidgetKit
import SwiftUI
import WebKit
#if os(iOS) || os(tvOS)
import UIKit
#else
import AppKit
#endif
#if os(iOS) || os(tvOS)
typealias PlatformViewRepresentable = UIViewRepresentable
#else
typealias PlatformViewRepresentable = NSViewRepresentable
#endif
struct HTMLClockView: PlatformViewRepresentable {
#if os(iOS) || os(tvOS)
func makeUIView(context: Context) -> WKWebView { createWebView() }
func updateUIView(_ uiView: WKWebView, context: Context) {}
#else
func makeNSView(context: Context) -> WKWebView { createWebView() }
func updateNSView(_ nsView: WKWebView, context: Context) {}
#endif
private func createWebView() -> WKWebView {
let webView = WKWebView()
#if os(iOS) || os(tvOS)
webView.isOpaque = false
webView.backgroundColor = .clear
webView.scrollView.isScrollEnabled = false
#else
webView.setValue(false, forKey: "drawsBackground")
webView.setValue(false, forKey: "isOpaque")
if let scrollView = webView.value(forKey: "scrollView") as? NSScrollView {
scrollView.hasVerticalScroller = false
scrollView.hasHorizontalScroller = false
}
#endif
if let url = Bundle.main.url(forResource: "analog-clock", withExtension: "html") {
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
}
return webView
}
}
struct Provider: AppIntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationAppIntent())
}
func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry {
SimpleEntry(date: Date(), configuration: configuration)
}
func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<SimpleEntry> {
let entry = SimpleEntry(date: Date(), configuration: configuration)
return Timeline(entries: [entry], policy: .never)
}
// func relevances() async -> WidgetRelevances<ConfigurationAppIntent> {
// // Generate a list containing the contexts this widget is relevant in.
// }
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationAppIntent
}
struct CoBiE_Analog_ClockEntryView : View {
var entry: Provider.Entry
var body: some View {
HTMLClockView()
}
}
struct CoBiE_Analog_Clock: Widget {
let kind: String = "CoBiE_Analog_Clock"
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in
CoBiE_Analog_ClockEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
}
}
@@ -0,0 +1,17 @@
//
// CoBiE_Analog_ClockBundle.swift
// CoBiE Analog Clock
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import WidgetKit
import SwiftUI
@main
struct CoBiE_Analog_ClockBundle: WidgetBundle {
var body: some Widget {
CoBiE_Analog_Clock()
CoBiE_Analog_ClockControl()
}
}
@@ -0,0 +1,77 @@
//
// CoBiE_Analog_ClockControl.swift
// CoBiE Analog Clock
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import AppIntents
import SwiftUI
import WidgetKit
struct CoBiE_Analog_ClockControl: ControlWidget {
static let kind: String = "no.kaizenkodo.CoBiE.CoBiE Analog Clock"
var body: some ControlWidgetConfiguration {
AppIntentControlConfiguration(
kind: Self.kind,
provider: Provider()
) { value in
ControlWidgetToggle(
"Start Timer",
isOn: value.isRunning,
action: StartTimerIntent(value.name)
) { isRunning in
Label(isRunning ? "On" : "Off", systemImage: "timer")
}
}
.displayName("Timer")
.description("A an example control that runs a timer.")
}
}
extension CoBiE_Analog_ClockControl {
struct Value {
var isRunning: Bool
var name: String
}
struct Provider: AppIntentControlValueProvider {
func previewValue(configuration: TimerConfiguration) -> Value {
CoBiE_Analog_ClockControl.Value(isRunning: false, name: configuration.timerName)
}
func currentValue(configuration: TimerConfiguration) async throws -> Value {
let isRunning = true // Check if the timer is running
return CoBiE_Analog_ClockControl.Value(isRunning: isRunning, name: configuration.timerName)
}
}
}
struct TimerConfiguration: ControlConfigurationIntent {
static let title: LocalizedStringResource = "Timer Name Configuration"
@Parameter(title: "Timer Name", default: "Timer")
var timerName: String
}
struct StartTimerIntent: SetValueIntent {
static let title: LocalizedStringResource = "Start a timer"
@Parameter(title: "Timer Name")
var name: String
@Parameter(title: "Timer is running")
var value: Bool
init() {}
init(_ name: String) {
self.name = name
}
func perform() async throws -> some IntentResult {
// Start the timer
return .result()
}
}
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<style>body{margin:0;background:transparent;}</style>
</head>
<body>
<div class="analog-clock-container">
<div id="clock">
<div class="hand megasequence" id="handMegasequence"></div>
<div class="hand eonstrip" id="handEonstrip"></div>
<div class="hand chronon" id="handChronon"></div>
<div class="hand quantic" id="handQuantic"></div>
<div class="hand xeno" id="handXeno"></div>
<div class="clock-center"></div>
<div class="clock-label">CoBiE Time</div>
</div>
</div>
<script src="cobie.js"></script>
<script src="clock.js"></script>
</body>
</html>
+204
View File
@@ -0,0 +1,204 @@
// Minimal CoBiE analog clock logic wrapped in its own scope to
// avoid clashes with variables from other scripts on the page.
(function () {
const {
COBIE_EPOCH,
COBIE_UNITS,
floorDiv,
getTAIOffsetAt,
toCobiets
} = window.Cobie;
function getMarkerOffset(width) {
const points = [
{ width: 1024, value: 2 },
{ width: 450, value: 1.3 },
{ width: 200, value: 0.8 }
];
// Sort points by width descending for easier handling
points.sort((a, b) => b.width - a.width);
for (let i = 0; i < points.length - 1; i++) {
const p1 = points[i];
const p2 = points[i + 1];
if (width <= p1.width && width >= p2.width) {
// Linear interpolation
const t = (width - p2.width) / (p1.width - p2.width);
return p2.value + t * (p1.value - p2.value);
}
}
// Extrapolation for width > max known
if (width > points[0].width) {
const p1 = points[0];
const p2 = points[1];
const slope = (p1.value - p2.value) / (p1.width - p2.width);
return p1.value + slope * (width - p1.width);
}
// Extrapolation for width < min known
const p1 = points[points.length - 2];
const p2 = points[points.length - 1];
const slope = (p2.value - p1.value) / (p2.width - p1.width);
return p2.value + slope * (width - p2.width);
}
function placeMarkers() {
const clock = document.getElementById('clock');
let markers = clock.querySelectorAll('.marker');
let ticks = clock.querySelectorAll('.tick');
// Create markers if they don't exist yet
if (markers.length === 0) {
for (let i = 0; i < 16; i++) {
const m = document.createElement('div');
m.className = 'marker';
m.textContent = i.toString(16).toUpperCase();
clock.appendChild(m);
}
markers = clock.querySelectorAll('.marker');
}
// Create tick marks once
if (ticks.length === 0) {
for (let i = 0; i < 256; i++) {
const t = document.createElement('div');
t.classList.add('tick');
if (i % 16 === 0) t.classList.add('big');
else if (i % 8 === 0) t.classList.add('mid');
// insert before markers so digits sit on top
clock.insertBefore(t, clock.firstChild);
}
ticks = clock.querySelectorAll('.tick');
}
// Unified radius based on the actual clock size
const baseRadius = clock.offsetWidth / 2;
// Tick lengths relative to the clock radius
const lenBig = baseRadius * 0.12;
const lenMid = baseRadius * 0.08;
const lenSmall = baseRadius * 0.05;
const outerR = baseRadius - 2; // just inside the border
// Distance from center for the marker digits so they sit just inside big ticks
const markerSize = markers[0] ? markers[0].offsetWidth : 0;
const markerRadius = outerR - lenBig - markerSize * getMarkerOffset(clock.offsetWidth);
markers.forEach((m, i) => {
const angle = (i / 16) * 2 * Math.PI;
m.style.left = '50%';
m.style.top = '50%';
m.style.transform =
`translate(-50%, -50%) rotate(${angle}rad) translate(0, -${markerRadius}px) rotate(${-angle}rad)`;
});
ticks.forEach((t, i) => {
let len = lenSmall;
if (t.classList.contains('big')) len = lenBig;
else if (t.classList.contains('mid')) len = lenMid;
const innerR = outerR - len;
const angle = ((i + 1) / 256) * 2 * Math.PI;
t.style.height = `${len}px`;
t.style.left = '50%';
t.style.top = '50%';
t.style.transform = `translate(-50%, 0) rotate(${angle}rad) translate(0, -${innerR}px)`;
if (clock.offsetWidth < 200 && !t.classList.contains('big') && !t.classList.contains('mid')) {
t.style.display = 'none';
} else {
t.style.display = '';
}
});
}
const lastAngles = {
handXeno: 0,
handQuantic: 0,
handChronon: 0,
handEonstrip: 0,
handMegasequence: 0
};
function rotateHand(id, angle) {
const el = document.getElementById(id);
if (!el) return;
const prev = lastAngles[id];
if (angle < prev) {
// When wrapping around (e.g. 15 → 0), animate to one full turn
// and then snap back to the new angle to avoid a jump.
const target = angle + 360;
const handle = () => {
el.removeEventListener('transitionend', handle);
// Snap back without animation
el.style.transition = 'none';
el.style.transform = `translateX(-50%) translateZ(0) rotate(${angle}deg)`;
void el.offsetWidth;
el.style.transition = '';
};
el.addEventListener('transitionend', handle, { once: true });
el.style.transform = `translateX(-50%) translateZ(0) rotate(${target}deg)`;
} else {
el.style.transform = `translateX(-50%) translateZ(0) rotate(${angle}deg)`;
}
lastAngles[id] = angle;
}
function renderClock(cob) {
// Use fractional progress within each unit so angles stay small
const xf = (cob % COBIE_UNITS.quantic) / COBIE_UNITS.quantic;
const qf = (cob % COBIE_UNITS.chronon) / COBIE_UNITS.chronon;
const cf = (cob % COBIE_UNITS.eonstrip) / COBIE_UNITS.eonstrip;
const ef = (cob % COBIE_UNITS.megasequence) / COBIE_UNITS.megasequence;
const mf = (cob % COBIE_UNITS.cosmocycle) / COBIE_UNITS.cosmocycle;
rotateHand('handXeno', xf * 360);
rotateHand('handQuantic', qf * 360);
rotateHand('handChronon', cf * 360);
rotateHand('handEonstrip', ef * 360);
rotateHand('handMegasequence', mf * 360);
}
function updateClock() {
renderClock(toCobiets(new Date()));
}
let intervalId = null;
function startClock() {
clearInterval(intervalId);
updateClock();
intervalId = setInterval(updateClock, 1000);
}
function showTime(cob) {
clearInterval(intervalId);
renderClock(cob);
}
function initClock() {
placeMarkers();
startClock();
const clk = document.getElementById('clock');
if (clk) {
clk.addEventListener('click', () => {
document.body.classList.toggle('fullscreen-clock');
// Re-position markers after toggling fullscreen
requestAnimationFrame(placeMarkers);
});
}
window.addEventListener('resize', placeMarkers);
window.CobieClock = {
start: startClock,
showTime
};
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initClock);
} else {
initClock();
}
})();
+185
View File
@@ -0,0 +1,185 @@
const COBIE_EPOCH = 0;
const COBIE_UNITS = {
second: 1,
xenocycle: 0x10,
quantic: 0x100,
chronon: 0x1000,
eonstrip: 0x10000,
megasequence: 0x100000,
cosmocycle: 0x1000000,
galactic_year: 0x10000000,
universal_eon: 0x100000000,
celestial_era: 0x1000000000,
epoch_of_cosmos: 0x10000000000,
cosmic_aeon: 0x100000000000,
metaepoch: 0x1000000000000,
eternum: 0x10000000000000,
infinitum: 0x100000000000000,
astralmillennia: 0x1000000000000000
};
function floorDiv(a, b) {
return Math.trunc(a / b);
}
function parseCobiets(str) {
const m = /^([+-]?)([0-9A-Fa-f]+)\.([0-9A-Fa-f]{1,})$/.exec(str.trim());
if (!m) return null;
const sign = m[1] === '-' ? -1 : 1;
const allDateKeys = [
'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon',
'epoch_of_cosmos','celestial_era','universal_eon','galactic_year',
'cosmocycle','megasequence','eonstrip'
];
let rawDateHex = m[2];
if (rawDateHex.length < allDateKeys.length) {
rawDateHex = rawDateHex.padStart(allDateKeys.length, '0');
}
let dateKeys = [...allDateKeys];
if (rawDateHex.length > allDateKeys.length) {
const extraCount = rawDateHex.length - allDateKeys.length;
for (let i = 0; i < extraCount; i++) {
dateKeys.unshift(null);
}
}
const timeHexRaw = m[3];
const timeHex = timeHexRaw.padStart(4, '0');
const timeKeys = ['chronon', 'quantic', 'xenocycle', 'second'];
let total = 0;
for (let i = 0; i < rawDateHex.length; i++) {
const digit = parseInt(rawDateHex[i], 16);
const key = dateKeys[i];
if (key === null) {
const power = rawDateHex.length - 1 - i;
total += digit * Math.pow(16, power) * COBIE_UNITS['eonstrip'];
} else {
total += digit * COBIE_UNITS[key];
}
}
timeHex.split('').forEach((h, i) => {
total += parseInt(h, 16) * COBIE_UNITS[timeKeys[i]];
});
return sign * total;
}
function getTAIOffsetAt(date) {
const taiEpoch = new Date('1958-01-01T00:00:00Z');
if (date < taiEpoch) { return 0; }
const leapSeconds = [
{ date: '1972-01-01T00:00:00Z', offset: 10 },
{ date: '1972-07-01T00:00:00Z', offset: 11 },
{ date: '1973-01-01T00:00:00Z', offset: 12 },
{ date: '1974-01-01T00:00:00Z', offset: 13 },
{ date: '1975-01-01T00:00:00Z', offset: 14 },
{ date: '1976-01-01T00:00:00Z', offset: 15 },
{ date: '1977-01-01T00:00:00Z', offset: 16 },
{ date: '1978-01-01T00:00:00Z', offset: 17 },
{ date: '1979-01-01T00:00:00Z', offset: 18 },
{ date: '1980-01-01T00:00:00Z', offset: 19 },
{ date: '1981-07-01T00:00:00Z', offset: 20 },
{ date: '1982-07-01T00:00:00Z', offset: 21 },
{ date: '1983-07-01T00:00:00Z', offset: 22 },
{ date: '1985-07-01T00:00:00Z', offset: 23 },
{ date: '1988-01-01T00:00:00Z', offset: 24 },
{ date: '1990-01-01T00:00:00Z', offset: 25 },
{ date: '1991-01-01T00:00:00Z', offset: 26 },
{ date: '1992-07-01T00:00:00Z', offset: 27 },
{ date: '1993-07-01T00:00:00Z', offset: 28 },
{ date: '1994-07-01T00:00:00Z', offset: 29 },
{ date: '1996-01-01T00:00:00Z', offset: 30 },
{ date: '1997-07-01T00:00:00Z', offset: 31 },
{ date: '1999-01-01T00:00:00Z', offset: 32 },
{ date: '2006-01-01T00:00:00Z', offset: 33 },
{ date: '2009-01-01T00:00:00Z', offset: 34 },
{ date: '2012-07-01T00:00:00Z', offset: 35 },
{ date: '2015-07-01T00:00:00Z', offset: 36 },
{ date: '2017-01-01T00:00:00Z', offset: 37 }
];
for (let i = 0; i < leapSeconds.length; i++) {
const leapDate = new Date(leapSeconds[i].date);
if (date < leapDate) {
return i === 0 ? 10 : leapSeconds[i - 1].offset;
}
}
return 37;
}
function toCobiets(date) {
const utcSec = floorDiv(date.getTime(), 1000);
const taiSec = utcSec + getTAIOffsetAt(date);
return taiSec - COBIE_EPOCH;
}
function fromCobiets(cobiets) {
const taiSeconds = cobiets + COBIE_EPOCH;
const taiMs = taiSeconds * 1000;
let utcMs = taiMs;
for (let i = 0; i < 3; i++) {
const off = getTAIOffsetAt(new Date(utcMs));
utcMs = taiMs - off * 1000;
}
return new Date(utcMs);
}
const UNIT_KEYS = [
'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon','epoch_of_cosmos','celestial_era','universal_eon','galactic_year','cosmocycle','megasequence','eonstrip','chronon','quantic','xenocycle','second'
];
function breakdownNonNeg(cob) {
let rem = cob, bd = {};
for (let key of UNIT_KEYS) {
bd[key] = floorDiv(rem, COBIE_UNITS[key]);
rem %= COBIE_UNITS[key];
}
return bd;
}
function formatCobieTimestamp(cobiets) {
const sign = cobiets < 0 ? '-' : '+';
const absCob = Math.abs(cobiets);
const bd = breakdownNonNeg(absCob);
const dateUnits = [
'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon','epoch_of_cosmos','celestial_era','universal_eon','galactic_year','cosmocycle','megasequence','eonstrip'
];
let rawDateHex = dateUnits.map(key => bd[key].toString(16)).join('');
rawDateHex = rawDateHex.replace(/^0+/, '');
if (rawDateHex === '') rawDateHex = '0';
const timeHex = [bd.chronon, bd.quantic, bd.xenocycle, bd.second]
.map(n => n.toString(16)).join('');
const paddedTimeHex = timeHex.padStart(4, '0');
return sign + rawDateHex + '.' + paddedTimeHex;
}
const Cobie = {
COBIE_EPOCH,
COBIE_UNITS,
floorDiv,
parseCobiets,
getTAIOffsetAt,
toCobiets,
fromCobiets,
formatCobieTimestamp,
breakdownNonNeg
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = Cobie;
}
// Expose globally when loaded in a browser environment
if (typeof window !== 'undefined') {
window.Cobie = Cobie;
}
@@ -0,0 +1,561 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%);
color: #e0e0e0;
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.tooltip {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 8px 12px;
border-radius: 4px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
z-index: 9999;
}
h1 {
font-size: 2.5em;
background: linear-gradient(45deg, #00ffff, #ff00ff);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 10px;
animation: glow 3s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { opacity: 0.8; }
50% { opacity: 1; }
}
.current-time {
background: rgba(0, 255, 255, 0.1);
border: 2px solid rgba(0, 255, 255, 0.3);
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
text-align: center;
position: relative;
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
height: var(--clock-size);
}
.current-time.manual::before {
animation-play-state: paused;
}
.current-time::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(0, 255, 255, 0.1), transparent);
transform: rotate(45deg);
animation: sweep 3s linear infinite;
}
@keyframes sweep {
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
}
.cobie-time {
font-size: 2.5em;
font-family: 'Courier New', monospace;
letter-spacing: 2px;
margin: 10px 0;
text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
}
.regular-time {
font-size: 1.2em;
color: #aaa;
}
.timezone-selector {
margin: 20px 0;
text-align: center;
}
select {
padding: 10px 20px;
font-size: 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 5px;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
}
select:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.calendar-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin: 20px 0;
}
button {
padding: 10px 20px;
font-size: 16px;
background: linear-gradient(45deg, #00ffff, #0080ff);
border: none;
border-radius: 5px;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
button::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
button:hover::before {
width: 300px;
height: 300px;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 255, 255, 0.3);
}
.calendar-view {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
overflow: hidden;
}
.calendar-header {
text-align: center;
font-size: 1.5em;
margin-bottom: 20px;
color: #00ffff;
}
.eonstrip-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
margin-bottom: 20px;
transform: translateX(0);
transition: transform 0.3s ease;
will-change: transform;
}
.eonstrip-card {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 10px;
padding: 15px;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: visible;
white-space: normal;
}
.eonstrip-card::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
opacity: 0;
transition: opacity 0.3s ease;
}
.eonstrip-card:hover::before {
opacity: 1;
}
.eonstrip-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 255, 255, 0.2);
border-color: rgba(0, 255, 255, 0.5);
z-index: 1000;
}
.eonstrip-card:hover .tooltip {
opacity: 1;
}
.eonstrip-card.current {
background: rgba(0, 255, 255, 0.2);
border-color: rgba(0, 255, 255, 0.5);
}
.eonstrip-name {
font-size: 1em;
font-weight: bold;
color: #00ffff;
margin-bottom: 5px;
}
.eonstrip-hex {
font-size: 0.85em; /* was default monospace size */
font-family: 'Courier New', monospace;
color: #ffaaff;
margin-bottom: 10px;
}
.eonstrip-dates {
font-size: 0.7em;
color: #aaa;
line-height: 1.4;
}
.event-tag {
display: inline-block;
margin-top: 4px;
padding: 2px 6px;
font-size: 0.75em;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, rgba(0,255,255,0.25), rgba(255,0,255,0.25));
border: 1px solid rgba(255,255,255,0.2);
border-radius: 4px;
text-shadow: 0 0 6px rgba(0,255,255,0.7);
}
.time-details {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
}
.time-unit {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.time-unit:last-child {
border-bottom: none;
}
.unit-name {
color: #00ffff;
font-weight: bold;
}
.unit-value {
font-family: 'Courier New', monospace;
color: #ffaaff;
}
.toggle-btn {
background: linear-gradient(45deg, #00ffff, #0080ff);
border: none;
border-radius: 8px;
color: #fff;
font-size: 0.9em;
padding: 8px 16px;
cursor: pointer;
position: relative;
overflow: hidden;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 0 10px rgba(0,255,255,0.4);
transition: all 0.3s ease;
}
.toggle-btn .arrow-icon {
display: inline-block;
margin-right: 6px;
transition: transform 0.3s ease;
}
.toggle-btn:hover {
transform: translateY(-2px);
box-shadow: 0 0 20px rgba(0,255,255,0.6);
}
.extended-section {
display: none; /* hidden by default */
margin-top: 10px;
animation: fadeIn 0.4s ease;
}
/* Simple fadein for when extended units show */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@media only screen
and (max-width: 812px) /* iPhone portrait widths go up to ~812px */
and (orientation: portrait) {
/* scale down your main text by 30% */
html {
font-size: 70%;
}
/* if you prefer targeting only the big “cobie-time” element: */
.cobie-time {
font-size: 1.75em; /* was 2.5em, which is 70% of 2.5em */
}
}
.eonstrip-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
transform: translateX(0);
transition: transform 0.3s ease;
will-change: transform;
}
/* Layout combining current time and analog clock */
.time-display {
--clock-size: 40vmin;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch;
gap: 20px;
}
.analog-clock-container {
flex: 0 0 auto;
width: var(--clock-size);
margin-left: auto;
display: flex;
justify-content: center;
align-items: center;
}
#clock {
position: relative;
width: var(--clock-size);
height: var(--clock-size);
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 50%;
background: radial-gradient(circle at center, #0a0e27 0%, #1a1f3a 100%);
box-shadow: 0 0 25px rgba(0, 255, 255, 0.2), inset 0 0 40px rgba(255, 0, 255, 0.2);
}
.clock-center {
position: absolute;
top: 50%;
left: 50%;
width: calc(var(--clock-size) * 0.13);
height: calc(var(--clock-size) * 0.13);
transform: translate(-50%, -50%) translateZ(0);
background: url('logo.svg') center/contain no-repeat;
background-color: transparent;
border-radius: 50%;
/* box-shadow: 0 0 8px rgba(0, 255, 255, 0.8); */
z-index: 2;
pointer-events: none;
}
@media screen and (min-width: 1200px) {
.clock-center {
width: calc(var(--clock-size) * 0.085);
height: calc(var(--clock-size) * 0.085);
}
}
.clock-label {
position: absolute;
bottom: 30%;
left: 50%;
transform: translateX(-50%);
font-family: 'Great Vibes', cursive;
font-size: calc(var(--clock-size) * 0.06);
color: #ffaaff;
text-shadow: 0 0 6px rgba(255, 0, 255, 0.6);
pointer-events: none;
z-index: 0;
}
body.fullscreen-clock .clock-label {
font-size: calc(var(--clock-size) * 0.08);
}
.marker {
position: absolute;
width: calc(var(--clock-size) * 0.13);
height: calc(var(--clock-size) * 0.13);
text-align: center;
line-height: calc(var(--clock-size) * 0.13);
/* Use a futuristic font for the clock markers */
font-family: 'Orbitron', 'Trebuchet MS', 'Lucida Sans', Arial, sans-serif;
font-size: calc(var(--clock-size) * 0.08);
font-weight: 600;
color: #00ffff;
background: none;
border: none;
border-radius: 0;
text-shadow: 0 0 6px rgba(0, 255, 255, 0.9), 0 0 12px rgba(0, 255, 255, 0.7);
box-shadow: none;
transform-origin: center;
font-variant-numeric: tabular-nums;
user-select: none;
pointer-events: none;
will-change: transform;
z-index: 1;
}
.tick {
position: absolute;
width: 2px;
background: #00ffff;
transform-origin: center top;
z-index: 0;
}
.tick.mid {
background: #66ffff;
}
.tick.big {
background: #ffffff;
}
.hand {
position: absolute;
bottom: 50%;
left: 50%;
transform-origin: bottom center;
transform: translateX(-50%);
transition: transform 0.5s ease-in-out;
border-radius: 2px;
z-index: 1;
}
.hand.xeno {
width: 2px;
height: 44%;
background: linear-gradient(to top, #66ccff, #0044ff);
box-shadow: 0 0 8px #66ccff;
}
.hand.quantic {
width: 3px;
height: 40%;
background: linear-gradient(to top, #ff66ff, #9900ff);
box-shadow: 0 0 8px #ff66ff;
}
.hand.chronon {
width: 4px;
height: 34%;
background: linear-gradient(to top, #ff4444, #880000);
box-shadow: 0 0 8px #ff4444;
}
.hand.eonstrip {
width: 5px;
height: 30%;
background: linear-gradient(to top, #33ff99, #006633);
box-shadow: 0 0 8px #33ff99;
}
.hand.megasequence {
width: 6px;
height: 26%;
background: linear-gradient(to top, #ffbb33, #aa5500);
box-shadow: 0 0 8px #ffbb33;
}
@media only screen and (max-height: 430px) and (orientation: landscape) {
.time-display {
--clock-size: 70vmin;
}
}
body.fullscreen-clock .header,
body.fullscreen-clock .current-time,
body.fullscreen-clock .timezone-selector,
body.fullscreen-clock .calendar-controls,
body.fullscreen-clock .calendar-view,
body.fullscreen-clock .time-details,
body.fullscreen-clock .explanations {
display: none;
}
body.fullscreen-clock .time-display {
position: fixed;
inset: 0;
justify-content: center;
align-items: center;
z-index: 1000;
}
body.fullscreen-clock .analog-clock-container {
--clock-size: 80vmin;
width: var(--clock-size);
margin-left: 0;
}
body.fullscreen-clock #clock {
width: var(--clock-size);
height: var(--clock-size);
}
+819
View File
@@ -0,0 +1,819 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
4FF813CC2E00403F00D89535 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FF813CB2E00403E00D89535 /* WidgetKit.framework */; };
4FF813CE2E00403F00D89535 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FF813CD2E00403F00D89535 /* SwiftUI.framework */; };
4FF813DD2E00404000D89535 /* CoBiE Analog ClockExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4FF813C92E00403E00D89535 /* CoBiE Analog ClockExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
4FF813E72E00404000D89535 /* analog-clock.html in Resources */ = {isa = PBXBuildFile; fileRef = 4FF813E32E00404000D89535 /* analog-clock.html */; };
4FF813E82E00404000D89535 /* clock.js in Resources */ = {isa = PBXBuildFile; fileRef = 4FF813E42E00404000D89535 /* clock.js */; };
4FF813E92E00404000D89535 /* cobie.js in Resources */ = {isa = PBXBuildFile; fileRef = 4FF813E52E00404000D89535 /* cobie.js */; };
4FF813EA2E00404000D89535 /* style.css in Resources */ = {isa = PBXBuildFile; fileRef = 4FF813E62E00404000D89535 /* style.css */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
4FF813A92E003FB600D89535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4FF813912E003FB400D89535 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4FF813982E003FB400D89535;
remoteInfo = CoBiE;
};
4FF813B32E003FB600D89535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4FF813912E003FB400D89535 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4FF813982E003FB400D89535;
remoteInfo = CoBiE;
};
4FF813DB2E00404000D89535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4FF813912E003FB400D89535 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4FF813C82E00403E00D89535;
remoteInfo = "CoBiE Analog ClockExtension";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
4FF813E22E00404000D89535 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
4FF813DD2E00404000D89535 /* CoBiE Analog ClockExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
4FF813992E003FB400D89535 /* CoBiE.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoBiE.app; sourceTree = BUILT_PRODUCTS_DIR; };
4FF813A82E003FB600D89535 /* CoBiETests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoBiETests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4FF813B22E003FB600D89535 /* CoBiEUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoBiEUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4FF813C92E00403E00D89535 /* CoBiE Analog ClockExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "CoBiE Analog ClockExtension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
4FF813CB2E00403E00D89535 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
4FF813CD2E00403F00D89535 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
4FF813E32E00404000D89535 /* analog-clock.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "analog-clock.html"; path = "CoBiE Analog Clock/Resources/analog-clock.html"; sourceTree = "<group>"; };
4FF813E42E00404000D89535 /* clock.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = "clock.js"; path = "CoBiE Analog Clock/Resources/clock.js"; sourceTree = "<group>"; };
4FF813E52E00404000D89535 /* cobie.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = "cobie.js"; path = "CoBiE Analog Clock/Resources/cobie.js"; sourceTree = "<group>"; };
4FF813E62E00404000D89535 /* style.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "style.css"; path = "CoBiE Analog Clock/Resources/style.css"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
4FF813DE2E00404000D89535 /* Exceptions for "CoBiE Analog Clock" folder in "CoBiE Analog ClockExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 4FF813C82E00403E00D89535 /* CoBiE Analog ClockExtension */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
4FF8139B2E003FB400D89535 /* CoBiE */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = CoBiE;
sourceTree = "<group>";
};
4FF813AB2E003FB600D89535 /* CoBiETests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = CoBiETests;
sourceTree = "<group>";
};
4FF813B52E003FB600D89535 /* CoBiEUITests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = CoBiEUITests;
sourceTree = "<group>";
};
4FF813CF2E00403F00D89535 /* CoBiE Analog Clock */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
4FF813DE2E00404000D89535 /* Exceptions for "CoBiE Analog Clock" folder in "CoBiE Analog ClockExtension" target */,
);
path = "CoBiE Analog Clock";
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
4FF813962E003FB400D89535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813A52E003FB600D89535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813AF2E003FB600D89535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813C62E00403E00D89535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4FF813CE2E00403F00D89535 /* SwiftUI.framework in Frameworks */,
4FF813CC2E00403F00D89535 /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4FF813902E003FB400D89535 = {
isa = PBXGroup;
children = (
4FF8139B2E003FB400D89535 /* CoBiE */,
4FF813AB2E003FB600D89535 /* CoBiETests */,
4FF813B52E003FB600D89535 /* CoBiEUITests */,
4FF813CF2E00403F00D89535 /* CoBiE Analog Clock */,
4FF813CA2E00403E00D89535 /* Frameworks */,
4FF8139A2E003FB400D89535 /* Products */,
);
sourceTree = "<group>";
};
4FF8139A2E003FB400D89535 /* Products */ = {
isa = PBXGroup;
children = (
4FF813992E003FB400D89535 /* CoBiE.app */,
4FF813A82E003FB600D89535 /* CoBiETests.xctest */,
4FF813B22E003FB600D89535 /* CoBiEUITests.xctest */,
4FF813C92E00403E00D89535 /* CoBiE Analog ClockExtension.appex */,
);
name = Products;
sourceTree = "<group>";
};
4FF813CA2E00403E00D89535 /* Frameworks */ = {
isa = PBXGroup;
children = (
4FF813CB2E00403E00D89535 /* WidgetKit.framework */,
4FF813CD2E00403F00D89535 /* SwiftUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
4FF813982E003FB400D89535 /* CoBiE */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4FF813BC2E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiE" */;
buildPhases = (
4FF813952E003FB400D89535 /* Sources */,
4FF813962E003FB400D89535 /* Frameworks */,
4FF813972E003FB400D89535 /* Resources */,
4FF813E22E00404000D89535 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
4FF813DC2E00404000D89535 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
4FF8139B2E003FB400D89535 /* CoBiE */,
);
name = CoBiE;
packageProductDependencies = (
);
productName = CoBiE;
productReference = 4FF813992E003FB400D89535 /* CoBiE.app */;
productType = "com.apple.product-type.application";
};
4FF813A72E003FB600D89535 /* CoBiETests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4FF813BF2E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiETests" */;
buildPhases = (
4FF813A42E003FB600D89535 /* Sources */,
4FF813A52E003FB600D89535 /* Frameworks */,
4FF813A62E003FB600D89535 /* Resources */,
);
buildRules = (
);
dependencies = (
4FF813AA2E003FB600D89535 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
4FF813AB2E003FB600D89535 /* CoBiETests */,
);
name = CoBiETests;
packageProductDependencies = (
);
productName = CoBiETests;
productReference = 4FF813A82E003FB600D89535 /* CoBiETests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
4FF813B12E003FB600D89535 /* CoBiEUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4FF813C22E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiEUITests" */;
buildPhases = (
4FF813AE2E003FB600D89535 /* Sources */,
4FF813AF2E003FB600D89535 /* Frameworks */,
4FF813B02E003FB600D89535 /* Resources */,
);
buildRules = (
);
dependencies = (
4FF813B42E003FB600D89535 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
4FF813B52E003FB600D89535 /* CoBiEUITests */,
);
name = CoBiEUITests;
packageProductDependencies = (
);
productName = CoBiEUITests;
productReference = 4FF813B22E003FB600D89535 /* CoBiEUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
4FF813C82E00403E00D89535 /* CoBiE Analog ClockExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4FF813DF2E00404000D89535 /* Build configuration list for PBXNativeTarget "CoBiE Analog ClockExtension" */;
buildPhases = (
4FF813C52E00403E00D89535 /* Sources */,
4FF813C62E00403E00D89535 /* Frameworks */,
4FF813C72E00403E00D89535 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
4FF813CF2E00403F00D89535 /* CoBiE Analog Clock */,
);
name = "CoBiE Analog ClockExtension";
packageProductDependencies = (
);
productName = "CoBiE Analog ClockExtension";
productReference = 4FF813C92E00403E00D89535 /* CoBiE Analog ClockExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
4FF813912E003FB400D89535 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 2600;
LastUpgradeCheck = 2600;
TargetAttributes = {
4FF813982E003FB400D89535 = {
CreatedOnToolsVersion = 26.0;
};
4FF813A72E003FB600D89535 = {
CreatedOnToolsVersion = 26.0;
TestTargetID = 4FF813982E003FB400D89535;
};
4FF813B12E003FB600D89535 = {
CreatedOnToolsVersion = 26.0;
TestTargetID = 4FF813982E003FB400D89535;
};
4FF813C82E00403E00D89535 = {
CreatedOnToolsVersion = 26.0;
};
};
};
buildConfigurationList = 4FF813942E003FB400D89535 /* Build configuration list for PBXProject "CoBiE" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 4FF813902E003FB400D89535;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 4FF8139A2E003FB400D89535 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
4FF813982E003FB400D89535 /* CoBiE */,
4FF813A72E003FB600D89535 /* CoBiETests */,
4FF813B12E003FB600D89535 /* CoBiEUITests */,
4FF813C82E00403E00D89535 /* CoBiE Analog ClockExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
4FF813972E003FB400D89535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813A62E003FB600D89535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813B02E003FB600D89535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813C72E00403E00D89535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4FF813E72E00404000D89535 /* analog-clock.html in Resources */,
4FF813E82E00404000D89535 /* clock.js in Resources */,
4FF813E92E00404000D89535 /* cobie.js in Resources */,
4FF813EA2E00404000D89535 /* style.css in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
4FF813952E003FB400D89535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813A42E003FB600D89535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813AE2E003FB600D89535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
4FF813C52E00403E00D89535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
4FF813AA2E003FB600D89535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4FF813982E003FB400D89535 /* CoBiE */;
targetProxy = 4FF813A92E003FB600D89535 /* PBXContainerItemProxy */;
};
4FF813B42E003FB600D89535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4FF813982E003FB400D89535 /* CoBiE */;
targetProxy = 4FF813B32E003FB600D89535 /* PBXContainerItemProxy */;
};
4FF813DC2E00404000D89535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4FF813C82E00403E00D89535 /* CoBiE Analog ClockExtension */;
targetProxy = 4FF813DB2E00404000D89535 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
4FF813BA2E003FB600D89535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
4FF813BB2E003FB600D89535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SWIFT_COMPILATION_MODE = wholemodule;
};
name = Release;
};
4FF813BD2E003FB600D89535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiE;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Debug;
};
4FF813BE2E003FB600D89535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiE;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Release;
};
4FF813C02E003FB600D89535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiETests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CoBiE.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CoBiE";
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Debug;
};
4FF813C12E003FB600D89535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiETests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CoBiE.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CoBiE";
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Release;
};
4FF813C32E003FB600D89535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiEUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
TEST_TARGET_NAME = CoBiE;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Debug;
};
4FF813C42E003FB600D89535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = no.kaizenkodo.CoBiEUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
TEST_TARGET_NAME = CoBiE;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Release;
};
4FF813E02E00404000D89535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "CoBiE Analog Clock/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CoBiE Analog Clock";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "no.kaizenkodo.CoBiE.CoBiE-Analog-Clock";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
4FF813E12E00404000D89535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8KC3NAN2CJ;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "CoBiE Analog Clock/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "CoBiE Analog Clock";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "no.kaizenkodo.CoBiE.CoBiE-Analog-Clock";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
4FF813942E003FB400D89535 /* Build configuration list for PBXProject "CoBiE" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4FF813BA2E003FB600D89535 /* Debug */,
4FF813BB2E003FB600D89535 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4FF813BC2E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiE" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4FF813BD2E003FB600D89535 /* Debug */,
4FF813BE2E003FB600D89535 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4FF813BF2E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiETests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4FF813C02E003FB600D89535 /* Debug */,
4FF813C12E003FB600D89535 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4FF813C22E003FB600D89535 /* Build configuration list for PBXNativeTarget "CoBiEUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4FF813C32E003FB600D89535 /* Debug */,
4FF813C42E003FB600D89535 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4FF813DF2E00404000D89535 /* Build configuration list for PBXNativeTarget "CoBiE Analog ClockExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4FF813E02E00404000D89535 /* Debug */,
4FF813E12E00404000D89535 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 4FF813912E003FB400D89535 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>CoBiE Analog ClockExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>CoBiE.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,85 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+32
View File
@@ -0,0 +1,32 @@
//
// CoBiEApp.swift
// CoBiE
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import SwiftUI
import SwiftData
@main
struct CoBiEApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
+66
View File
@@ -0,0 +1,66 @@
//
// ContentView.swift
// CoBiE
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
} label: {
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
.onDelete(perform: deleteItems)
}
#if os(macOS)
.navigationSplitViewColumnWidth(min: 180, ideal: 200)
#endif
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
+18
View File
@@ -0,0 +1,18 @@
//
// Item.swift
// CoBiE
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import Foundation
import SwiftData
@Model
final class Item {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
+16
View File
@@ -0,0 +1,16 @@
//
// CoBiETests.swift
// CoBiETests
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import Testing
struct CoBiETests {
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
}
}
+41
View File
@@ -0,0 +1,41 @@
//
// CoBiEUITests.swift
// CoBiEUITests
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import XCTest
final class CoBiEUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
@MainActor
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
@MainActor
func testLaunchPerformance() throws {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
@@ -0,0 +1,33 @@
//
// CoBiEUITestsLaunchTests.swift
// CoBiEUITests
//
// Created by Oleksandr Kozachuk on 2025-06-16.
//
import XCTest
final class CoBiEUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
@MainActor
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}
+8 -9
View File
@@ -63,18 +63,17 @@ An interactive web app that visualizes the **CosmoChron Binary Epoch (CoBiE)** t
└── assets/ # (Optional) images or external CSS/JS
```
## macOS Dashboard Widget
## macOS Widget
The repository includes a minimal Dashboard widget under
`macos-widget/`. A helper script `build-widget.sh` is provided to
package it automatically:
The repository provides a SwiftUI widget inside the Xcode project
located at `CoBiE/CoBiE.xcodeproj`.
1. Run `./build-widget.sh` from the repository root.
2. The script creates `CoBiEClock.wdgt` which you can double-click to
install.
1. Open the project in Xcode.
2. Select the `CoBiE Analog Clock` scheme.
3. Build and run to install the widget on macOS 13 or later.
The widget runs offline and shows the analog CoBiE clock on your
desktop.
The widget displays the analog CoBiE clock directly on your desktop or
in Notification Center.
## Contributing
-23
View File
@@ -1,23 +0,0 @@
#!/bin/bash
# Build the macOS Dashboard widget without duplicating code
set -euo pipefail
WIDGET_NAME="CoBiEClock"
SRC_DIR="macos-widget"
BUILD_DIR="build-widget"
# Clean build directory
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
# Copy unique widget files
cp "$SRC_DIR/Info.plist" "$SRC_DIR/index.html" "$BUILD_DIR/"
# Copy shared files from repository root
cp clock.js cobie.js style.css logo.svg "$BUILD_DIR/"
# Create archive and rename
zip -r "${WIDGET_NAME}.zip" "$BUILD_DIR" > /dev/null
mv "${WIDGET_NAME}.zip" "${WIDGET_NAME}.wdgt"
echo "Widget created: ${WIDGET_NAME}.wdgt"
+1 -1
View File
@@ -32,7 +32,7 @@
<div class="hand quantic" id="handQuantic"></div>
<div class="hand xeno" id="handXeno"></div>
<div class="clock-center"></div>
<div class="clock-label">CoBiE Time</div>
<div class="clock-label">CoBiE</div>
</div>
</div>
</div>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>CoBiE Clock</string>
<key>CFBundleIdentifier</key>
<string>com.example.cobieclock</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>MainHTML</key>
<string>index.html</string>
</dict>
</plist>
-23
View File
@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CoBiE Clock</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="analog-clock-container">
<div id="clock">
<div class="hand megasequence" id="handMegasequence"></div>
<div class="hand eonstrip" id="handEonstrip"></div>
<div class="hand chronon" id="handChronon"></div>
<div class="hand quantic" id="handQuantic"></div>
<div class="hand xeno" id="handXeno"></div>
<div class="clock-center"></div>
<div class="clock-label">CoBiE Time</div>
</div>
</div>
<script src="cobie.js"></script>
<script src="clock.js"></script>
</body>
</html>
+4 -4
View File
@@ -439,13 +439,13 @@ body.fullscreen-clock .clock-label {
.marker {
position: absolute;
width: 2em;
height: 2em;
width: calc(var(--clock-size) * 0.13);
height: calc(var(--clock-size) * 0.13);
text-align: center;
line-height: 2em;
line-height: calc(var(--clock-size) * 0.13);
/* Use a futuristic font for the clock markers */
font-family: 'Orbitron', 'Trebuchet MS', 'Lucida Sans', Arial, sans-serif;
font-size: 1.2em;
font-size: calc(var(--clock-size) * 0.08);
font-weight: 600;
color: #00ffff;
background: none;