Commit ab6ccec56e
Verified · cmc
Layout: unified · split
Sources/OrgEditorAppKit/Theme.swift +1 −1
| @@ -89,7 +89,7 @@ struct Theme { | |||
| 89 | if let custom = spec.todo[keyword] { | 89 | if let custom = spec.todo[keyword] { |
| 90 | storage.addAttribute(.foregroundColor, value: Self.dynamic(custom, custom), range: range) | 90 | storage.addAttribute(.foregroundColor, value: Self.dynamic(custom, custom), range: range) |
| 91 | } else { | 91 | } else { |
| 92 | fg(role == .todo ? "todo" : "done") | 92 | fg(ThemeSpec.TodoClass.of(keyword, isDone: role == .done).key) |
| 93 | } | 93 | } |
| 94 | case .priority: fg("priority") | 94 | case .priority: fg("priority") |
| 95 | case .tags: fg("tags") | 95 | case .tags: fg("tags") |
Sources/OrgPresentation/AgendaRows.swift +23
| @@ -76,6 +76,8 @@ public struct AgendaRow: Sendable, Equatable, Identifiable { | |||
| 76 | public let priority: String? | 76 | public let priority: String? |
| 77 | /// The statistics cookie: `1/3`, `50%`. | 77 | /// The statistics cookie: `1/3`, `50%`. |
| 78 | public let progress: String? | 78 | public let progress: String? |
| 79 | /// Log mode: the time clocked (`1:30`) or the state set (`DONE`). | ||
| 80 | public let detail: String? | ||
| 79 | /// `HHMM` of the start and the end, when timed. | 81 | /// `HHMM` of the start and the end, when timed. |
| 80 | public let start: Int? | 82 | public let start: Int? |
| 81 | public let end: Int? | 83 | public let end: Int? |
| @@ -146,6 +148,7 @@ public struct AgendaRow: Sendable, Equatable, Identifiable { | |||
| 146 | self.isDone = item.isDone | 148 | self.isDone = item.isDone |
| 147 | self.priority = item.priorityLetter | 149 | self.priority = item.priorityLetter |
| 148 | self.progress = item.progress | 150 | self.progress = item.progress |
| 151 | self.detail = item.kind == .clock || item.kind == .state ? Self.logDetail(item.extra) : nil | ||
| 149 | self.start = timed?.0 | 152 | self.start = timed?.0 |
| 150 | self.end = timed?.1 | 153 | self.end = timed?.1 |
| 151 | self.planningDay = planning | 154 | self.planningDay = planning |
| @@ -184,6 +187,7 @@ public struct AgendaRow: Sendable, Equatable, Identifiable { | |||
| 184 | self.isDone = false | 187 | self.isDone = false |
| 185 | self.priority = nil | 188 | self.priority = nil |
| 186 | self.progress = nil | 189 | self.progress = nil |
| 190 | self.detail = nil | ||
| 187 | self.start = start | 191 | self.start = start |
| 188 | self.end = end | 192 | self.end = end |
| 189 | self.planningDay = nil | 193 | self.planningDay = nil |
| @@ -192,8 +196,17 @@ public struct AgendaRow: Sendable, Equatable, Identifiable { | |||
| 192 | } | 196 | } |
| 193 | 197 | ||
| 194 | static let rangeDay = try! NSRegularExpression(pattern: "^\\(([0-9]+/[0-9]+)\\)") | 198 | static let rangeDay = try! NSRegularExpression(pattern: "^\\(([0-9]+/[0-9]+)\\)") |
| 199 | static let logParen = try! NSRegularExpression(pattern: "\\(([^)]+)\\)") | ||
| 195 | static let rangeEnd = try! NSRegularExpression(pattern: "-\\+?([0-9]{1,2}):([0-9]{2})") | 200 | static let rangeEnd = try! NSRegularExpression(pattern: "-\\+?([0-9]{1,2}):([0-9]{2})") |
| 196 | 201 | ||
| 202 | /// The text in parentheses of a log row's `extra`: `Clocked: (1:30)`. | ||
| 203 | static func logDetail(_ extra: String) -> String? { | ||
| 204 | let ns = extra as NSString | ||
| 205 | guard let m = logParen.firstMatch(in: extra, range: NSRange(location: 0, length: ns.length)) else { return nil } | ||
| 206 | let text = ns.substring(with: m.range(at: 1)).trimmingCharacters(in: .whitespaces) | ||
| 207 | return text.isEmpty ? nil : text | ||
| 208 | } | ||
| 209 | |||
| 197 | /// The end of a `%t` time range (` 9:00-10:30`), as `HHMM`; past midnight (`+1:00`) adds 24 hours. | 210 | /// The end of a `%t` time range (` 9:00-10:30`), as `HHMM`; past midnight (`+1:00`) adds 24 hours. |
| 198 | static func endTime(_ time: String) -> Int? { | 211 | static func endTime(_ time: String) -> Int? { |
| 199 | let ns = time as NSString | 212 | let ns = time as NSString |
| @@ -224,6 +237,16 @@ public struct AgendaDayGroup: Sendable, Equatable, Identifiable { | |||
| 224 | public var id: Int { day } | 237 | public var id: Int { day } |
| 225 | public var isEmpty: Bool { overdue.isEmpty && rows.isEmpty } | 238 | public var isEmpty: Bool { overdue.isEmpty && rows.isEmpty } |
| 226 | 239 | ||
| 240 | /// `Today`, `Tomorrow`, `Yesterday`, or nil. | ||
| 241 | public func relativeName(today: Int) -> String? { | ||
| 242 | switch day - today { | ||
| 243 | case 0: "Today" | ||
| 244 | case 1: "Tomorrow" | ||
| 245 | case -1: "Yesterday" | ||
| 246 | default: nil | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 227 | /// Midnight starting the day. | 250 | /// Midnight starting the day. |
| 228 | public func date(calendar: Calendar) -> Date { | 251 | public func date(calendar: Calendar) -> Date { |
| 229 | let d = Days.date(day) | 252 | let d = Days.date(day) |
Sources/Orgstar/AgendaView.swift +3 −12
| @@ -64,7 +64,7 @@ struct AgendaView: View { | |||
| 64 | @State private var spanKind: AgendaSpan? | 64 | @State private var spanKind: AgendaSpan? |
| 65 | @State private var showingDatePicker = false | 65 | @State private var showingDatePicker = false |
| 66 | @State private var pickedDate = Date() | 66 | @State private var pickedDate = Date() |
| 67 | @State private var overdueShown = true | 67 | @AppStorage("agendaOverdueExpanded") private var overdueShown = true |
| 68 | @State private var hovered: String? | 68 | @State private var hovered: String? |
| 69 | @FocusState private var focused: Bool | 69 | @FocusState private var focused: Bool |
| 70 | @AppStorage("agendaSpan") private var span = 10 | 70 | @AppStorage("agendaSpan") private var span = 10 |
| @@ -350,7 +350,7 @@ struct AgendaView: View { | |||
| 350 | let past = group.day < agenda.today | 350 | let past = group.day < agenda.today |
| 351 | let color = group.isToday ? todayColor : past ? Color.secondary : theme.color("agenda-date") ?? .primary | 351 | let color = group.isToday ? todayColor : past ? Color.secondary : theme.color("agenda-date") ?? .primary |
| 352 | return header { | 352 | return header { |
| 353 | if let relative = Self.relativeName(group.day, today: agenda.today) { | 353 | if let relative = group.relativeName(today: agenda.today) { |
| 354 | Text(relative) | 354 | Text(relative) |
| 355 | .font(.system(size: 13, weight: .bold)) | 355 | .font(.system(size: 13, weight: .bold)) |
| 356 | .foregroundStyle(color) | 356 | .foregroundStyle(color) |
| @@ -614,15 +614,6 @@ struct AgendaView: View { | |||
| 614 | return formatter.string(from: date(day)) | 614 | return formatter.string(from: date(day)) |
| 615 | } | 615 | } |
| 616 | 616 | ||
| 617 | static func relativeName(_ day: Int, today: Int) -> String? { | ||
| 618 | switch day - today { | ||
| 619 | case 0: "Today" | ||
| 620 | case 1: "Tomorrow" | ||
| 621 | case -1: "Yesterday" | ||
| 622 | default: nil | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | static func hhmm(_ date: Date) -> Int { | 617 | static func hhmm(_ date: Date) -> Int { |
| 627 | let c = Calendar.current.dateComponents([.hour, .minute], from: date) | 618 | let c = Calendar.current.dateComponents([.hour, .minute], from: date) |
| 628 | return (c.hour ?? 0) * 100 + (c.minute ?? 0) | 619 | return (c.hour ?? 0) * 100 + (c.minute ?? 0) |
| @@ -1170,7 +1161,7 @@ struct AgendaRowView: View { | |||
| 1170 | .foregroundStyle(row.isDone ? AnyShapeStyle(.secondary) : AnyShapeStyle(theme.color("foreground") ?? .primary)) | 1161 | .foregroundStyle(row.isDone ? AnyShapeStyle(.secondary) : AnyShapeStyle(theme.color("foreground") ?? .primary)) |
| 1171 | .lineLimit(1) | 1162 | .lineLimit(1) |
| 1172 | .truncationMode(.tail) | 1163 | .truncationMode(.tail) |
| 1173 | if let progress = row.progress { | 1164 | ForEach([row.progress, row.detail].compactMap { $0 }, id: \.self) { progress in |
| 1174 | Text(progress) | 1165 | Text(progress) |
| 1175 | .font(.system(size: 10, weight: .semibold).monospacedDigit()) | 1166 | .font(.system(size: 10, weight: .semibold).monospacedDigit()) |
| 1176 | .foregroundStyle(.secondary) | 1167 | .foregroundStyle(.secondary) |
Sources/OrgstarMobile/AgendaScreen.swift +16 −6
| @@ -17,6 +17,7 @@ struct AgendaScreen: View { | |||
| 17 | @Environment(ClockModel.self) private var clock | 17 | @Environment(ClockModel.self) private var clock |
| 18 | @Environment(MobileModel.self) private var model | 18 | @Environment(MobileModel.self) private var model |
| 19 | @Environment(\.colorScheme) private var colorScheme | 19 | @Environment(\.colorScheme) private var colorScheme |
| 20 | @Environment(\.openURL) private var openURL | ||
| 20 | @State private var tick = 0 | 21 | @State private var tick = 0 |
| 21 | /// The time the now line shows, kept to the minute while the agenda is open. | 22 | /// The time the now line shows, kept to the minute while the agenda is open. |
| 22 | @State private var now = Date() | 23 | @State private var now = Date() |
| @@ -261,10 +262,11 @@ struct AgendaScreen: View { | |||
| 261 | 262 | ||
| 262 | private func header(_ group: AgendaDayGroup) -> some View { | 263 | private func header(_ group: AgendaDayGroup) -> some View { |
| 263 | let day = date(group.day).formatted(.dateTime.weekday(.abbreviated).day().month(.abbreviated)) | 264 | let day = date(group.day).formatted(.dateTime.weekday(.abbreviated).day().month(.abbreviated)) |
| 265 | let color = group.isToday ? todayColor : group.day < agenda.today ? Color.secondary : themed(theme.color("agenda-date", dark: isDark), .primary) | ||
| 264 | return HStack(alignment: .firstTextBaseline) { | 266 | return HStack(alignment: .firstTextBaseline) { |
| 265 | Text(group.isToday ? "Today · " + day : day) | 267 | Text(group.relativeName(today: agenda.today).map { $0 + " · " + day } ?? day) |
| 266 | .font(.subheadline.weight(group.isToday ? .bold : .semibold)) | 268 | .font(.subheadline.weight(group.isToday ? .bold : .semibold)) |
| 267 | .foregroundStyle(group.isToday ? todayColor : .primary) | 269 | .foregroundStyle(color) |
| 268 | Spacer() | 270 | Spacer() |
| 269 | Button("Add to \(day)", systemImage: "plus.circle") { model.captureRequest = CaptureRequest(day: group.day) } | 271 | Button("Add to \(day)", systemImage: "plus.circle") { model.captureRequest = CaptureRequest(day: group.day) } |
| 270 | .labelStyle(.iconOnly) | 272 | .labelStyle(.iconOnly) |
| @@ -305,7 +307,7 @@ struct AgendaScreen: View { | |||
| 305 | } | 307 | } |
| 306 | 308 | ||
| 307 | private func weekStart(_ day: Int) -> Int { | 309 | private func weekStart(_ day: Int) -> Int { |
| 308 | day - (Days.weekday(day) - (Calendar.current.firstWeekday - 1) + 7) % 7 | 310 | day - (Days.weekday(day) + 6) % 7 |
| 309 | } | 311 | } |
| 310 | 312 | ||
| 311 | /// Shows `range` around `day`; nil returns to the configured span. | 313 | /// Shows `range` around `day`; nil returns to the configured span. |
| @@ -403,6 +405,14 @@ struct AgendaScreen: View { | |||
| 403 | Group { | 405 | Group { |
| 404 | if let item = row.item, let path = item.path, let offset = item.headingOffset ?? item.markerOffset { | 406 | if let item = row.item, let path = item.path, let offset = item.headingOffset ?? item.markerOffset { |
| 405 | NavigationLink(value: ReaderRoute.offset(path, offset)) { AgendaRowView(row: row, day: day, theme: theme, dark: isDark) } | 407 | NavigationLink(value: ReaderRoute.offset(path, offset)) { AgendaRowView(row: row, day: day, theme: theme, dark: isDark) } |
| 408 | } else if let event = row.event { | ||
| 409 | // Calendar can't be asked for one event by URL; it opens on the event's day. | ||
| 410 | Button { | ||
| 411 | if let url = URL(string: "calshow:\(event.start.timeIntervalSinceReferenceDate)") { openURL(url) } | ||
| 412 | } label: { | ||
| 413 | AgendaRowView(row: row, day: day, theme: theme, dark: isDark) | ||
| 414 | } | ||
| 415 | .tint(.primary) | ||
| 406 | } else { | 416 | } else { |
| 407 | AgendaRowView(row: row, day: day, theme: theme, dark: isDark) | 417 | AgendaRowView(row: row, day: day, theme: theme, dark: isDark) |
| 408 | } | 418 | } |
| @@ -416,7 +426,7 @@ struct AgendaScreen: View { | |||
| 416 | .swipeActions(edge: .leading) { | 426 | .swipeActions(edge: .leading) { |
| 417 | if canReschedule(row), let item = row.item { | 427 | if canReschedule(row), let item = row.item { |
| 418 | Button("Tomorrow", systemImage: "sunrise") { reschedule(row, item, to: agenda.today + 1) }.tint(.orange) | 428 | Button("Tomorrow", systemImage: "sunrise") { reschedule(row, item, to: agenda.today + 1) }.tint(.orange) |
| 419 | Button("Next Week", systemImage: "calendar.badge.plus") { reschedule(row, item, to: agenda.today + 7) }.tint(.indigo) | 429 | Button("Next Week", systemImage: "calendar.badge.plus") { reschedule(row, item, to: weekStart(agenda.today) + 7) }.tint(.indigo) |
| 420 | Button("Pick Date", systemImage: "calendar") { rescheduling = row }.tint(.gray) | 430 | Button("Pick Date", systemImage: "calendar") { rescheduling = row }.tint(.gray) |
| 421 | } | 431 | } |
| 422 | } | 432 | } |
| @@ -426,7 +436,7 @@ struct AgendaScreen: View { | |||
| 426 | if canReschedule(row) { | 436 | if canReschedule(row) { |
| 427 | Menu("Reschedule", systemImage: "calendar.badge.clock") { | 437 | Menu("Reschedule", systemImage: "calendar.badge.clock") { |
| 428 | Button("Tomorrow") { reschedule(row, item, to: agenda.today + 1) } | 438 | Button("Tomorrow") { reschedule(row, item, to: agenda.today + 1) } |
| 429 | Button("Next Week") { reschedule(row, item, to: agenda.today + 7) } | 439 | Button("Next Week") { reschedule(row, item, to: weekStart(agenda.today) + 7) } |
| 430 | Button("Pick Date…") { rescheduling = row } | 440 | Button("Pick Date…") { rescheduling = row } |
| 431 | } | 441 | } |
| 432 | } | 442 | } |
| @@ -572,7 +582,7 @@ struct AgendaRowView: View { | |||
| 572 | Circle().fill(categoryColor).frame(width: 7, height: 7) | 582 | Circle().fill(categoryColor).frame(width: 7, height: 7) |
| 573 | Text(row.category).lineLimit(1).layoutPriority(1) | 583 | Text(row.category).lineLimit(1).layoutPriority(1) |
| 574 | } | 584 | } |
| 575 | if let progress = row.progress { | 585 | ForEach([row.progress, row.detail].compactMap { $0 }, id: \.self) { progress in |
| 576 | Text(progress).monospacedDigit() | 586 | Text(progress).monospacedDigit() |
| 577 | .padding(.horizontal, 5) | 587 | .padding(.horizontal, 5) |
| 578 | .background(.quaternary.opacity(0.7), in: Capsule()) | 588 | .background(.quaternary.opacity(0.7), in: Capsule()) |
Sources/OrgstarMobile/SettingsScreen.swift +11 −2
| @@ -32,6 +32,12 @@ struct SettingsScreen: View { | |||
| 32 | } footer: { | 32 | } footer: { |
| 33 | Text("A folder with config.toml and capture.toml, such as a synced copy of ~/.config/orgstar from your Mac. Changes to them apply as they sync.") | 33 | Text("A folder with config.toml and capture.toml, such as a synced copy of ~/.config/orgstar from your Mac. Changes to them apply as they sync.") |
| 34 | } | 34 | } |
| 35 | Section("Agenda") { | ||
| 36 | let _ = model.settingsVersion | ||
| 37 | Toggle("Show days without entries", isOn: Binding { | ||
| 38 | OrgPreferences.agendaShowAllDates() | ||
| 39 | } set: { model.set("agendaShowAllDates", to: .bool($0)) }) | ||
| 40 | } | ||
| 35 | calendarSection | 41 | calendarSection |
| 36 | Section("In use") { | 42 | Section("In use") { |
| 37 | let _ = model.settingsVersion | 43 | let _ = model.settingsVersion |
| @@ -101,9 +107,12 @@ struct SettingsScreen: View { | |||
| 101 | } | 107 | } |
| 102 | 108 | ||
| 103 | /// Shows or hides a calendar's events; with every calendar checked the setting is empty. | 109 | /// Shows or hides a calendar's events; with every calendar checked the setting is empty. |
| 110 | /// The last checked calendar stays checked, as an empty list means every calendar. | ||
| 104 | private func toggle(_ title: String, shown: Bool, chosen: [String]) { | 111 | private func toggle(_ title: String, shown: Bool, chosen: [String]) { |
| 105 | var titles = chosen.isEmpty ? calendars.map(\.title) : chosen | 112 | var titles = calendars.filter { CalendarEvents.chooses(id: $0.id, title: $0.title, chosen: chosen) }.map(\.title) |
| 106 | if shown { titles.removeAll { $0 == title } } else { titles.append(title) } | 113 | titles.removeAll { $0 == title } |
| 114 | if !shown { titles.append(title) } | ||
| 115 | guard !titles.isEmpty else { return } | ||
| 107 | let value = Set(titles) == Set(calendars.map(\.title)) ? "" : titles.joined(separator: ", ") | 116 | let value = Set(titles) == Set(calendars.map(\.title)) ? "" : titles.joined(separator: ", ") |
| 108 | model.set("calendarEventCalendars", to: .string(value)) | 117 | model.set("calendarEventCalendars", to: .string(value)) |
| 109 | } | 118 | } |
Tests/OrgPresentationTests/AgendaRowsTests.swift +20
| @@ -86,6 +86,26 @@ struct AgendaRowsTests { | |||
| 86 | #expect(!rent.isEvent && rent.item != nil) | 86 | #expect(!rent.isEvent && rent.item != nil) |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | @Test func logRowsKeepTheDurationAndState() { | ||
| 90 | let text = """ | ||
| 91 | * DONE Write report | ||
| 92 | CLOSED: [2026-10-05 Mon 11:00] | ||
| 93 | :LOGBOOK: | ||
| 94 | - State "DONE" from "TODO" [2026-10-05 Mon 11:00] | ||
| 95 | CLOCK: [2026-10-05 Mon 09:00]--[2026-10-05 Mon 10:30] => 1:30 | ||
| 96 | :END: | ||
| 97 | """ | ||
| 98 | var options = AgendaOptions() | ||
| 99 | options.logMode = true | ||
| 100 | options.logItems = ["closed", "clock", "state"] | ||
| 101 | let source = AgendaSource(path: "w.org", text: text, defaults: OrgSettings.default.withTodoKeywords("TODO | DONE")!) | ||
| 102 | let items = Agenda.list([source], start: Self.today, days: 1, today: Self.today, now: (12, 0), options: options)[0].items | ||
| 103 | let rows = items.compactMap { AgendaRow(item: $0, day: Self.today, today: Self.today) } | ||
| 104 | #expect(rows.first { $0.item?.kind == .clock }?.detail == "1:30") | ||
| 105 | #expect(rows.first { $0.item?.kind == .state }?.detail == "DONE") | ||
| 106 | #expect(rows.first { $0.item?.kind == .closed }?.detail == nil) | ||
| 107 | } | ||
| 108 | |||
| 89 | @Test func guttersOnOtherDays() { | 109 | @Test func guttersOnOtherDays() { |
| 90 | let later = Self.rows(day: Self.today + 4) | 110 | let later = Self.rows(day: Self.today + 4) |
| 91 | #expect(later["Renew passport"]?.gutter == "Due" && later["Renew passport"]?.status == .normal) | 111 | #expect(later["Renew passport"]?.gutter == "Due" && later["Renew passport"]?.status == .normal) |
docs/manual/guide/07-agenda.org +7 −7
| @@ -106,13 +106,13 @@ A time can come from the timestamp (=<2026-10-05 Mon 14:00-15:30>=) or from the | |||
| 106 | 106 | ||
| 107 | ** Today, overdue entries and empty days | 107 | ** Today, overdue entries and empty days |
| 108 | 108 | ||
| 109 | Days without entries are left out. Today always shows, with =Nothing scheduled= when it has no entries. To show every day of the span, as Org does by default, turn on Settings ▸ Agenda ▸ Show days without entries, or set this in =config.toml=: | 109 | Days without entries are left out. Today always shows, with =Nothing scheduled= when it has no entries. To show every day of the span, as Org does by default, turn on Settings ▸ Agenda ▸ Show days without entries (on iOS, Settings ▸ Agenda), or set this in =config.toml=: |
| 110 | 110 | ||
| 111 | #+BEGIN_SRC toml | 111 | #+BEGIN_SRC toml |
| 112 | org-agenda-show-all-dates = true | 112 | org-agenda-show-all-dates = true |
| 113 | #+END_SRC | 113 | #+END_SRC |
| 114 | 114 | ||
| 115 | Today's past deadlines and entries scheduled on earlier days, habits excepted, go in a group headed =Overdue= with their count, above today's other entries. The oldest date comes first; entries with the same date keep Org's order. Click or tap the header to collapse or expand the group. | 115 | Today's past deadlines and entries scheduled on earlier days, habits excepted, go in a group headed =Overdue= with their count, above today's other entries. The oldest date comes first; entries with the same date keep Org's order. Click or tap the header to collapse or expand the group; it stays as you left it. |
| 116 | 116 | ||
| 117 | On today, a line marked with the current time sits between the timed entries: before the first entry that starts later, or after the last one when all have started. It shows only when today has timed entries, as Org's =now= line does. Org's time grid (the lines at 8:00, 10:00 and so on) isn't shown. | 117 | On today, a line marked with the current time sits between the timed entries: before the first entry that starts later, or after the last one when all have started. It shows only when today has timed entries, as Org's =now= line does. Org's time grid (the lines at 8:00, 10:00 and so on) isn't shown. |
| 118 | 118 | ||
| @@ -125,7 +125,7 @@ Days are sorted as =org-agenda-sorting-strategy= sorts them by default for the a | |||
| 125 | Press =l= to turn log mode (=org-agenda-log-mode=) on or off. Each day then also shows: | 125 | Press =l= to turn log mode (=org-agenda-log-mode=) on or off. Each day then also shows: |
| 126 | 126 | ||
| 127 | - =Closed= entries whose =CLOSED:= timestamp is on that day; | 127 | - =Closed= entries whose =CLOSED:= timestamp is on that day; |
| 128 | - =Clocked= entries for each =CLOCK:= line that starts on that day, with the clock's start and end times in the left column. | 128 | - =Clocked= entries for each =CLOCK:= line that starts on that day, with the clock's start and end times in the left column and the time clocked, such as =1:30=, after the title. |
| 129 | 129 | ||
| 130 | A note under a clock line (a list item directly below it) is added to the title after a =-=. These are the default =org-agenda-log-mode-items=, =(closed clock)=; state changes are not shown and there is no setting for that. | 130 | A note under a clock line (a list item directly below it) is added to the title after a =-=. These are the default =org-agenda-log-mode-items=, =(closed clock)=; state changes are not shown and there is no setting for that. |
| 131 | 131 | ||
| @@ -530,14 +530,14 @@ The board is not available on iOS. | |||
| 530 | The Agenda tab shows the same views from the same files, using the settings in the synced =config.toml= and the views in =views.toml= (see [[file:14-ios.org][iOS]]). Differences from the Mac: | 530 | The Agenda tab shows the same views from the same files, using the settings in the synced =config.toml= and the views in =views.toml= (see [[file:14-ios.org][iOS]]). Differences from the Mac: |
| 531 | 531 | ||
| 532 | - The Views menu, at the top left, lists the built-in views, your saved views, and Tags and Properties…, which asks for a match string. There is no TODO-only match from the menu; use =/!= in the match or a =todo-match= view. | 532 | - The Views menu, at the top left, lists the built-in views, your saved views, and Tags and Properties…, which asks for a match string. There is no TODO-only match from the menu; use =/!= in the match or a =todo-match= view. |
| 533 | - A bar floats at the bottom of the screen with Earlier, Filter, Today, Go to Date, the span and Later. The span menu switches between Day, Week, Month and the configured span (=10 Days= by default). Weeks start on the first day of the week in the device's region settings. In the TODO list and match views the bar has Filter and Agenda, which returns to the day view. Messages show above the bar; tap one to dismiss it. | 533 | - A bar floats at the bottom of the screen with Earlier, Filter, Today, Go to Date, the span and Later. The span menu switches between Day, Week, Month and the configured span (=10 Days= by default). Weeks start on Monday, as on the Mac. In the TODO list and match views the bar has Filter and Agenda, which returns to the day view. Messages show above the bar; tap one to dismiss it. |
| 534 | - The title names the days shown: =Thu 8 Oct=, =Week of 5 Oct=, =October 2026=, or =5 Oct – 14 Oct=. | 534 | - The title names the days shown: =Thu 8 Oct=, =Week of 5 Oct=, =October 2026=, or =5 Oct – 14 Oct=. |
| 535 | - Day headers read =Today · Thu 8 Oct= or =Fri 9 Oct=, with a + that captures onto that day. The Overdue group stays collapsed or expanded as you left it. | 535 | - Day headers read =Today · Thu 8 Oct=, =Tomorrow · Fri 9 Oct= or =Mon 12 Oct=, with a + that captures onto that day. Days before today are grey. |
| 536 | - Pull down to refresh. | 536 | - Pull down to refresh. |
| 537 | - The Filter button opens a sheet: type a filter as for =/=, or tap a tag or category to cycle between Keep, Leave out and no filter. | 537 | - The Filter button opens a sheet: type a filter as for =/=, or tap a tag or category to cycle between Keep, Leave out and no filter. |
| 538 | - Tap an entry to open it. Calendar events don't open. | 538 | - Tap an entry to open it. Tap a calendar event to open Calendar on the event's day; iOS can't open Calendar at one event. |
| 539 | - Swipe left on an open TODO entry for Done, which sets the first done keyword of its sequence, and Next State, which moves to the next keyword. The sequence is the one the file defines: the default keywords from =config.toml=, the file's own =#+TODO= lines, and those of its setup files, with unsaved edits in an open buffer included. | 539 | - Swipe left on an open TODO entry for Done, which sets the first done keyword of its sequence, and Next State, which moves to the next keyword. The sequence is the one the file defines: the default keywords from =config.toml=, the file's own =#+TODO= lines, and those of its setup files, with unsaved edits in an open buffer included. |
| 540 | - Swipe right on a scheduled entry, a deadline, a habit or a TODO entry, when it isn't done, for Tomorrow, Next Week and Pick Date. Tomorrow and Next Week are one day and seven days after today. On a deadline row these move the deadline; on other rows they set the scheduled date. A timed row keeps its times. | 540 | - Swipe right on a scheduled entry, a deadline, a habit or a TODO entry, when it isn't done, for Tomorrow, Next Week and Pick Date. Tomorrow is the day after today and Next Week is next Monday. On a deadline row these move the deadline; on other rows they set the scheduled date. A timed row keeps its times. |
| 541 | - Touch and hold an entry for TODO State…, Reschedule (Tomorrow, Next Week, Pick Date…), Schedule…, Deadline…, Tags…, Priority…, Clock In, Refile… and Archive…. | 541 | - Touch and hold an entry for TODO State…, Reschedule (Tomorrow, Next Week, Pick Date…), Schedule…, Deadline…, Tags…, Priority…, Clock In, Refile… and Archive…. |
| 542 | - Marking an entry done and rescheduling one give haptic feedback. | 542 | - Marking an entry done and rescheduling one give haptic feedback. |
| 543 | - There are no keyboard commands, log mode or bulk marks. | 543 | - There are no keyboard commands, log mode or bulk marks. |
docs/manual/guide/13-configuration.org +1 −1
| @@ -493,7 +493,7 @@ A keyword without its own color takes its class's color. Case doesn't matter in | |||
| 493 | | Other open | =todo= | Any other keyword that isn't done | | 493 | | Other open | =todo= | Any other keyword that isn't done | |
| 494 | | Other done | =done= | Any other done keyword | | 494 | | Other done | =done= | Any other done keyword | |
| 495 | 495 | ||
| 496 | The classes color keywords in the agenda and its widgets on both platforms, and in the iOS editor and reader. The Mac editor colors keywords with =todo= and =done= only. | 496 | The classes color keywords in the editors, the iOS reader, the agenda and its widgets. |
| 497 | 497 | ||
| 498 | A category without its own color gets a hue worked out from its name, the same on every run and device, at a lightness that reads on the appearance's background. | 498 | A category without its own color gets a hue worked out from its name, the same on every run and device, at a lightness that reads on the appearance's background. |
| 499 | 499 | ||
docs/manual/guide/14-ios.org +3 −2
| @@ -160,7 +160,7 @@ The Agenda tab shows the agenda over all your folders, with the span and start d | |||
| 160 | - + on a day's header :: captures an entry scheduled on that day (see [[file:08-capture.org][Capture]]). | 160 | - + on a day's header :: captures an entry scheduled on that day (see [[file:08-capture.org][Capture]]). |
| 161 | - Tap :: an entry to read it, at its heading. | 161 | - Tap :: an entry to read it, at its heading. |
| 162 | - Swipe left :: on a TODO entry for Done, which sets the first done keyword of its sequence, or Next State. | 162 | - Swipe left :: on a TODO entry for Done, which sets the first done keyword of its sequence, or Next State. |
| 163 | - Swipe right :: for Tomorrow, Next Week or Pick Date, which move the entry's scheduled date, or its deadline on a deadline row. | 163 | - Swipe right :: for Tomorrow, Next Week (next Monday) or Pick Date, which move the entry's scheduled date, or its deadline on a deadline row. |
| 164 | - Touch and hold :: an entry for TODO State…, Reschedule, Schedule…, Deadline…, Tags…, Priority…, Clock In, Refile…, and Archive…. | 164 | - Touch and hold :: an entry for TODO State…, Reschedule, Schedule…, Deadline…, Tags…, Priority…, Clock In, Refile…, and Archive…. |
| 165 | 165 | ||
| 166 | Commands from the agenda change the file directly when it isn't open, and through the editor when it is. If the heading changed since the agenda was built, nothing runs. | 166 | Commands from the agenda change the file directly when it isn't open, and through the editor when it is. If the heading changed since the agenda was built, nothing runs. |
| @@ -311,8 +311,9 @@ Changes to the files apply as they sync, and again each time you return to the a | |||
| 311 | 311 | ||
| 312 | Stop Using This Folder returns every setting to its default. | 312 | Stop Using This Folder returns every setting to its default. |
| 313 | 313 | ||
| 314 | The Calendar section is the one place the app changes a setting itself: | 314 | The Agenda and Calendar sections are the only places the app changes a setting itself: |
| 315 | 315 | ||
| 316 | - Show days without entries :: turns =org-agenda-show-all-dates= on or off. | ||
| 316 | - Show Calendar Events :: turns on =calendar-events=, and asks for access to your calendars. iOS asks for full access, as Calendar has no read-only level; Orgstar only reads. While access is missing the section shows Allow Access to Calendars…; if you declined, allow it in the Settings app under Privacy & Security ▸ Calendars. | 317 | - Show Calendar Events :: turns on =calendar-events=, and asks for access to your calendars. iOS asks for full access, as Calendar has no read-only level; Orgstar only reads. While access is missing the section shows Allow Access to Calendars…; if you declined, allow it in the Settings app under Privacy & Security ▸ Calendars. |
| 317 | - The calendars :: tap one to show or hide its events (=calendar-event-calendars=). With every calendar checked the setting is empty, so calendars added later show too. | 318 | - The calendars :: tap one to show or hide its events (=calendar-event-calendars=). With every calendar checked the setting is empty, so calendars added later show too. |
| 318 | 319 | ||