2026 Renovation (#3)

Rebuilt using raylib immediate mode gui. Simple window mode with text input and progress graph, and overlay mode.

Reviewed-on: #3
Co-authored-by: talksik <[email protected]>
This commit was merged in pull request #3.
This commit is contained in:
2026-10-03 20:20:17 +00:00
committed by talksik
parent 7be066b7d5
commit 0b09ef40b0
36 changed files with 6988 additions and 2858 deletions
Vendored
BIN
View File
Binary file not shown.
+5 -1
View File
@@ -1,2 +1,6 @@
build/ build/
build-universal/ *~
*.o
*#*
adhd
*.sqlite3
-76
View File
@@ -1,76 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(adhd VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set minimum macOS version
# Qt 6.8.6 requires macOS 11.0 (Big Sur) minimum
if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS version")
endif()
find_package(Qt6 REQUIRED COMPONENTS
Core
Gui
Widgets
)
qt_standard_project_setup()
qt_add_executable(adhd
main.cpp
mainwindow.cpp
mainwindow.h
service.cpp
service.h
mainwindow.ui
sessionslistmodel.h
sessionslistmodel.cpp
sessionitemdelegate.h
sessionitemdelegate.cpp
createsessiondialog.h
createsessiondialog.cpp
createsessiondialog.ui
focusoverlay.h
focusoverlay.cpp
focusoverlay.ui
endsessiondialog.h
endsessiondialog.cpp
endsessiondialog.ui
namegenerator.h
namegenerator.cpp
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(adhd PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.adhd
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE $<NOT:$<CONFIG:Debug>>
)
target_link_libraries(adhd
PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
include(GNUInstallDirs)
install(TARGETS adhd
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
qt_generate_deploy_app_script(
TARGET adhd
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})
-462
View File
@@ -1,462 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 16.0.2, 2026-02-01T13:41:28. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{1354daad-1138-41d2-985c-27357b7ee48a}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">5</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Qt 6.8.6 for macOS</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Qt 6.8.6 for macOS</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt6.686.clang_64_kit</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=Debug
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_GENERATOR:STRING=Ninja
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="CMake.Build.Type">Release</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=Release
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_GENERATOR:STRING=Ninja
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_GENERATOR:STRING=Ninja
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-RelWithDebInfo</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release with Debug Information</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.3">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_GENERATOR:STRING=Ninja
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.4">
<value type="QString" key="CMake.Build.Type">MinSizeRel</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_BUILD_TYPE:STRING=MinSizeRel
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_GENERATOR:STRING=Ninja
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-MinSizeRel</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Minimum Size Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">5</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString"></value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Install Application Manager package</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">adhd</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">adhd</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Debug/adhd.app/Contents/MacOS</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>
+32
View File
@@ -0,0 +1,32 @@
CC = cc
CFLAGS = -g -Wall -Iinclude
SOURCES = src/main.c src/window_state.c
OBJECTS = src/main.o src/window_state.o
LDFLAGS = -lraylib -lm -lsqlite3
HEADERS = \
include/raygui.h include/common.h include/window_state.h
PREFIX ?= /usr/local
BINDIR := $(PREFIX)/bin
# adhd: $(OBJECTS)
# $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
# %.o: %.c $(HEADERS)
# $(CC) $(CFLAGS) -c $< -o $@
adhd: src/main.o src/window_state.o
$(CC) $(CFLAGS) src/main.o src/window_state.o -o $@ $(LDFLAGS)
src/main.o: src/main.c $(HEADERS)
$(CC) -c $(CFLAGS) src/main.c -o $@
src/window_state.o: src/window_state.c $(HEADERS)
$(CC) -c $(CFLAGS) src/window_state.c -o $@
clean:
rm -rf $(OBJECTS) adhd
.PHONY: install
install: adhd
install -D adhd $(BINDIR)/adhd
+13 -17
View File
@@ -1,33 +1,29 @@
# Flowy.adhd # Flowy.adhd
A macOS focus app that helps you stay on track by setting one intention per session. We live in a noisy digital world. Unless you turn off your WiFi, millions of companies bid for your attention as you try to perform even the simplest of tasks.
This tool is a free, open-source application to help you stay on track. You set an intention before you work, and an overlay will remind you of your intention every x minutes.
![app](demo/app.png) ![app](demo/app.png)
![desktop](demo/desktop.png) ![desktop](demo/desktop.png)
## Install
At the moment, you must build and install the application yourself. There are minimum dependencies, so you essentially just need a C compiler.
## How it works ## How it works
1. Set your intention when you sit down at your computer 1. Set your intention when you sit down at your computer
2. A persistent overlay keeps your focus visible while you work 2. A persistent overlay reminds you of your intention
3. End the session and mark it as a win or loss 3. Set whether today was a win (focused) or loss (distracted), and view your daily activity in a nice graph
4. Build a log of your sessions over time
## Philosophy ## Philosophy
Stay aware of your intentions to avoid distractions. No todo manager needed—just focus on winning each session, one thing at a time. In a world of fancy todo managers, for anyone with adhd, doing one thing at a time is the key. Win one intention at a time instead of planning, prioritizing, and juggling 100s of things.
## Features ## Features
- **No sign in required** - Start using immediately - **No sign in required**
- **Privacy first** - Sessions stored locally as markdown files - **Slick overlay to show current intention**
- **Lightweight** - Written in C with few dependencies, nothing bloated like electron
- **Privacy first** - No telemetry, works offline
- **Session history** - Review your past wins and losses - **Session history** - Review your past wins and losses
## Download
Get the latest version from [releases](https://github.com/flowy-live/adhd/releases).
## Roadmap
- View past session notes
- Win/loss statistics visualizer
- Windows and Linux support
+8 -40
View File
@@ -1,45 +1,13 @@
## Todo
- [ ] tab widget: one for settings, one for sessions, one for active session editing
- [ ] active session view would show timer, pause, end, and edit session details
- [ ] create new session button
- [ ] click on a session to set as active
- [ ] create separate overlay that the main app can listen to and notify of active session changes - [x] model window state with intention and simple overlay, window modes
## Ideas - [x] create 2d grid
- Show me all sessions and the current session - [x] usability: show week of the year / day of week, tooltips
- Sum up how many sessions I won/lost - [x] toggle win / loss and persist values
- A session can simply be described by simple paragraph of what you'll do, maybe markdown
- Less rigidity in the sense of typing out thoughts and session goals...less due dates. Capture first, do later.
- Later: allow notes within a session, jot things down, all live in the same folder
- It's like a todo.md
- Every session is based on week # - year - session x
- Every session is an individual document that lives on disk
- See which week of the year that I'm in
- See progress bar of how much of the year is done or percentage
- Show stats on how many sessions total I've won/lost
- Display a separate overlay window that shows me my active working item in center bottom in a pill
- Can commit to github for "sync" and even include an input url to auto-upload the data directory to github
- overlay can show the first line of the session doc (or separate input) - [x] persist results in sqlite3
- review of session can be: `Win`, `Neutral`, or `Loss`
## Flow - [ ] reminder animation
- select location to save all your session data in markdown files
- data in the app parses the markdown file and uses it as the database, including the frontmatter
1. At the start of the day/session, set an intention by creating a session - [ ] install via homebrew
2. Enter the top things to do this session in plain english (list or paragraph in markdown) - [ ] launch on hackernews "for developers with adhd"
3. Click start session, and clock runs. Can pause, or stop.
4. When done for the session, select "Close session"...it will ask you to grade the session
5. View previous sessions in a list
## marketing
- local-only
- access files and metadata
- simple video demo OR flowy research-site page that has simple visuals
## Launch
- [ ] install via brew
- [ ] open source github
- [ ] hackernews "for developers with adhd"...purist site
-141
View File
@@ -1,141 +0,0 @@
#!/bin/bash
# Build script for universal macOS bundle (arm64 + x86_64)
set -e # Exit on error
echo "========================================="
echo "Building Universal macOS Bundle for adhd"
echo "========================================="
# Configuration
APP_NAME="adhd"
BUILD_DIR="build-universal"
BUILD_TYPE="Release"
ARCHITECTURES="arm64;x86_64"
QT_PATH="$HOME/Qt/6.8.6/macos"
APPLE_DEVELOPMENT_IDENTITY="Developer ID Application: Arjun Patel (RK9WWM4Q99)"
# Clean previous build (optional - comment out to keep incremental builds)
# rm -rf "$BUILD_DIR"
# Configure CMake
echo ""
echo "Configuring CMake for universal binary..."
echo "Minimum macOS version: 11.0 (Big Sur)"
cmake -B "$BUILD_DIR" \
-DCMAKE_OSX_ARCHITECTURES="$ARCHITECTURES" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="11.0" \
-DCMAKE_PREFIX_PATH="$QT_PATH" \
-DCMAKE_BUILD_TYPE="$BUILD_TYPE"
# Build
echo ""
echo "Building..."
cmake --build "$BUILD_DIR" --config "$BUILD_TYPE" -j$(sysctl -n hw.ncpu)
# Verify the binary is universal
echo ""
echo "Verifying binary architectures..."
if [ -f "$BUILD_DIR/$APP_NAME.app/Contents/MacOS/$APP_NAME" ]; then
lipo -info "$BUILD_DIR/$APP_NAME.app/Contents/MacOS/$APP_NAME"
echo "✓ Binary is universal"
else
echo "✗ Error: Binary not found at expected location"
exit 1
fi
# Deploy Qt dependencies
echo ""
echo "Deploying Qt dependencies..."
$QT_PATH/bin/macdeployqt "$BUILD_DIR/$APP_NAME.app" -always-overwrite -verbose=1
echo "✓ Qt dependencies deployed"
# Code signing
echo ""
echo "========================================="
echo "Listing identities for code-signing"
echo "========================================="
security find-identity -v -p codesigning
echo ""
echo "========================================="
echo "Code signing .app bundle"
echo "========================================="
echo "Using: $APPLE_DEVELOPMENT_IDENTITY"
cd "$BUILD_DIR"
codesign --deep --force --timestamp --verify --verbose=1 \
--sign "$APPLE_DEVELOPMENT_IDENTITY" \
--options runtime "$APP_NAME.app"
echo ""
echo "Verifying code signature..."
codesign --verify --deep --strict --verbose=1 "./$APP_NAME.app"
echo "✓ Code signing complete"
# Create DMG
echo ""
echo "========================================="
echo "Creating DMG"
echo "========================================="
echo "NOTE: Install create-dmg if needed: https://github.com/sindresorhus/create-dmg"
if ! command -v create-dmg &> /dev/null; then
echo "⚠ Warning: create-dmg not found. Install with: brew install create-dmg"
echo "Skipping DMG creation, notarization, and stapling."
cd ..
echo ""
echo "========================================="
echo "Build completed (without DMG/notarization)"
echo "App bundle location: $BUILD_DIR/$APP_NAME.app"
echo "========================================="
exit 0
fi
create-dmg "$APP_NAME.app" --identity="$APPLE_DEVELOPMENT_IDENTITY" --dmg-title="$APP_NAME"
# WORKAROUND: https://github.com/sindresorhus/create-dmg/issues/52
mv *.dmg "$APP_NAME.dmg" 2>/dev/null || true
echo "✓ DMG created: $APP_NAME.dmg"
# Notarization
echo ""
echo "========================================="
echo "Starting Notarization"
echo "========================================="
echo "NOTE: Make sure you've run 'xcrun notarytool store-credentials' first"
xcrun notarytool submit "$APP_NAME.dmg" \
--keychain-profile "default" \
--wait \
--timeout "10m"
echo ""
echo "========================================="
echo "Stapling notarization ticket"
echo "========================================="
xcrun stapler staple -v "$APP_NAME.dmg"
echo "✓ Notarization complete"
# Optional: Create zip
echo ""
read -p "Create zip file? (y/N): " CREATE_ZIP
if [[ "$CREATE_ZIP" == "y" || "$CREATE_ZIP" == "Y" ]]; then
ZIP_NAME="$APP_NAME-universal.zip"
zip -9 "$ZIP_NAME" "$APP_NAME.dmg"
echo "✓ Created: $ZIP_NAME"
fi
cd ..
echo ""
echo "========================================="
echo "Build completed successfully!"
echo "========================================="
echo "App bundle: $BUILD_DIR/$APP_NAME.app"
echo "DMG: $BUILD_DIR/$APP_NAME.dmg"
echo ""
echo "To test: open $BUILD_DIR/$APP_NAME.dmg"
echo "========================================="
-40
View File
@@ -1,40 +0,0 @@
#include "createsessiondialog.h"
#include "ui_createsessiondialog.h"
#include <QPushButton>
CreateSessionDialog::CreateSessionDialog(QWidget *parent)
: QDialog(parent),
ui(new Ui::CreateSessionDialog)
{
ui->setupUi(this);
setWindowTitle("Create New Session");
// Disable OK button initially
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
// Connect oneliner field to validation
connect(ui->onelinerEdit, &QLineEdit::textChanged,
this, &CreateSessionDialog::onOnelinerChanged);
}
CreateSessionDialog::~CreateSessionDialog()
{
delete ui;
}
QString CreateSessionDialog::getOneliner() const
{
return ui->onelinerEdit->text().trimmed();
}
QString CreateSessionDialog::getContent() const
{
return ui->contentEdit->toPlainText().trimmed();
}
void CreateSessionDialog::onOnelinerChanged(const QString &text)
{
bool isValid = !text.trimmed().isEmpty();
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isValid);
}
-28
View File
@@ -1,28 +0,0 @@
#ifndef CREATESESSIONDIALOG_H
#define CREATESESSIONDIALOG_H
#include <QDialog>
namespace Ui {
class CreateSessionDialog;
}
class CreateSessionDialog : public QDialog
{
Q_OBJECT
public:
explicit CreateSessionDialog(QWidget *parent = nullptr);
~CreateSessionDialog();
QString getOneliner() const;
QString getContent() const;
private slots:
void onOnelinerChanged(const QString &text);
private:
Ui::CreateSessionDialog *ui;
};
#endif // CREATESESSIONDIALOG_H
-96
View File
@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateSessionDialog</class>
<widget class="QDialog" name="CreateSessionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Session</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="onelinerLabel">
<property name="text">
<string>Oneliner:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="onelinerEdit">
<property name="placeholderText">
<string>What will you focus on?</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="contentLabel">
<property name="text">
<string>Initial Content:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTextEdit" name="contentEdit">
<property name="placeholderText">
<string>Optional: Add initial notes or context...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CreateSessionDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CreateSessionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
-68
View File
@@ -1,68 +0,0 @@
#include "endsessiondialog.h"
#include "ui_endsessiondialog.h"
EndSessionDialog::EndSessionDialog(const QString &oneliner, int elapsedMs, QWidget *parent)
: QDialog(parent),
ui(new Ui::EndSessionDialog),
m_selectedResult(Session::UNSPECIFIED)
{
ui->setupUi(this);
setWindowTitle("End Session");
// Set oneliner and elapsed time
ui->onelinerLabel->setText(oneliner);
int seconds = elapsedMs / 1000;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
QString timeStr = QString("%1:%2:%3")
.arg(hours, 2, 10, QChar('0'))
.arg(minutes, 2, 10, QChar('0'))
.arg(secs, 2, 10, QChar('0'));
ui->elapsedLabel->setText("Duration: " + timeStr);
// Connect buttons
connect(ui->winButton, &QPushButton::clicked,
this, &EndSessionDialog::onWinClicked);
connect(ui->lossButton, &QPushButton::clicked,
this, &EndSessionDialog::onLossClicked);
connect(ui->neutralButton, &QPushButton::clicked,
this, &EndSessionDialog::onNeutralClicked);
}
EndSessionDialog::~EndSessionDialog()
{
delete ui;
}
Session::Result EndSessionDialog::getResult() const
{
return m_selectedResult;
}
QString EndSessionDialog::getAdditionalNotes() const
{
return ui->notesEdit->toPlainText().trimmed();
}
void EndSessionDialog::onWinClicked()
{
m_selectedResult = Session::WIN;
accept();
}
void EndSessionDialog::onLossClicked()
{
m_selectedResult = Session::LOSS;
accept();
}
void EndSessionDialog::onNeutralClicked()
{
m_selectedResult = Session::NEUTRAL;
accept();
}
-32
View File
@@ -1,32 +0,0 @@
#ifndef ENDSESSIONDIALOG_H
#define ENDSESSIONDIALOG_H
#include <QDialog>
#include "service.h"
namespace Ui {
class EndSessionDialog;
}
class EndSessionDialog : public QDialog
{
Q_OBJECT
public:
explicit EndSessionDialog(const QString &oneliner, int elapsedMs, QWidget *parent = nullptr);
~EndSessionDialog();
Session::Result getResult() const;
QString getAdditionalNotes() const;
private slots:
void onWinClicked();
void onLossClicked();
void onNeutralClicked();
private:
Ui::EndSessionDialog *ui;
Session::Result m_selectedResult;
};
#endif // ENDSESSIONDIALOG_H
-221
View File
@@ -1,221 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EndSessionDialog</class>
<widget class="QDialog" name="EndSessionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>End Session</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>How did your session go?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="onelinerLabel">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>Session Oneliner</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="elapsedLabel">
<property name="text">
<string>Duration: 00:00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="winButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #2ecc71;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #27ae60;
}
QPushButton:pressed {
background-color: #229954;
}</string>
</property>
<property name="text">
<string>WIN</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="lossButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #e74c3c;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #c0392b;
}
QPushButton:pressed {
background-color: #a93226;
}</string>
</property>
<property name="text">
<string>LOSS</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="neutralButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #95a5a6;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #7f8c8d;
}
QPushButton:pressed {
background-color: #707b7c;
}</string>
</property>
<property name="text">
<string>NEUTRAL</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Optional: Add final notes</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="notesEdit">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="placeholderText">
<string>Any final thoughts or notes about this session...</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
-100
View File
@@ -1,100 +0,0 @@
#include "focusoverlay.h"
#include "ui_focusoverlay.h"
#include <QScreen>
#include <QGuiApplication>
#include <QMouseEvent>
FocusOverlay::FocusOverlay(QWidget *parent)
: QWidget(parent),
ui(new Ui::FocusOverlay),
m_dragging(false)
{
ui->setupUi(this);
// Set window flags for always-on-top frameless window
// Use Qt::Window instead of Qt::Tool so it stays visible when app loses focus
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_ShowWithoutActivating); // Don't steal focus when showing
// Connect buttons
connect(ui->resetButton, &QPushButton::clicked,
this, &FocusOverlay::onResetButtonClicked);
connect(ui->endButton, &QPushButton::clicked,
this, &FocusOverlay::onEndButtonClicked);
// Position window
positionAtBottomCenter();
}
FocusOverlay::~FocusOverlay()
{
delete ui;
}
void FocusOverlay::setOneliner(const QString &oneliner)
{
ui->onelinerLabel->setText(oneliner);
}
void FocusOverlay::setElapsedTime(int elapsedMs)
{
int seconds = elapsedMs / 1000;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
QString timeStr = QString("%1:%2:%3")
.arg(hours, 2, 10, QChar('0'))
.arg(minutes, 2, 10, QChar('0'))
.arg(secs, 2, 10, QChar('0'));
ui->elapsedLabel->setText("Elapsed: " + timeStr);
}
void FocusOverlay::onResetButtonClicked()
{
emit resetClicked();
}
void FocusOverlay::onEndButtonClicked()
{
emit endSessionClicked();
}
void FocusOverlay::positionAtBottomCenter()
{
QScreen *screen = QGuiApplication::primaryScreen();
if (!screen) return;
QRect screenGeometry = screen->geometry();
int x = (screenGeometry.width() - width()) / 2;
int y = screenGeometry.height() - height();
move(x, y);
}
void FocusOverlay::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_dragging = true;
m_dragStartPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
event->accept();
}
}
void FocusOverlay::mouseMoveEvent(QMouseEvent *event)
{
if (m_dragging && (event->buttons() & Qt::LeftButton)) {
move(event->globalPosition().toPoint() - m_dragStartPosition);
event->accept();
}
}
void FocusOverlay::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_dragging = false;
event->accept();
}
}
-43
View File
@@ -1,43 +0,0 @@
#ifndef FOCUSOVERLAY_H
#define FOCUSOVERLAY_H
#include <QWidget>
namespace Ui {
class FocusOverlay;
}
class FocusOverlay : public QWidget
{
Q_OBJECT
public:
explicit FocusOverlay(QWidget *parent = nullptr);
~FocusOverlay();
void setOneliner(const QString &oneliner);
void setElapsedTime(int elapsedMs);
signals:
void resetClicked();
void endSessionClicked();
private slots:
void onResetButtonClicked();
void onEndButtonClicked();
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
Ui::FocusOverlay *ui;
void positionAtBottomCenter();
// For dragging
bool m_dragging;
QPoint m_dragStartPosition;
};
#endif // FOCUSOVERLAY_H
-158
View File
@@ -1,158 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FocusOverlay</class>
<widget class="QWidget" name="FocusOverlay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>102</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>80</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>350</width>
<height>102</height>
</size>
</property>
<property name="windowTitle">
<string>Focus Session</string>
</property>
<property name="styleSheet">
<string notr="true">QWidget#FocusOverlay {
background-color: rgba(50, 50, 50, 200);
border-radius: 10px;
border: 2px solid rgba(100, 100, 100, 255);
}
QLabel {
color: white;
}
QPushButton {
background-color: rgba(80, 80, 80, 255);
color: white;
border: 1px solid rgba(120, 120, 120, 255);
border-radius: 5px;
padding: 4px 8px;
min-width: 50px;
}
QPushButton:hover {
background-color: rgba(100, 100, 100, 255);
}
QPushButton:pressed {
background-color: rgba(60, 60, 60, 255);
}</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>8</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="onelinerLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Session Oneliner</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="elapsedLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Elapsed: 00:00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
<string>Reset</string>
</property>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="endButton">
<property name="text">
<string>End Session</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+15
View File
@@ -0,0 +1,15 @@
#ifndef COMMON_H
#define COMMON_H
typedef unsigned int U32;
struct date {
U32 day;
U32 month;
U32 year;
};
struct date to_date(char *);
char *to_string(struct date);
#endif
+6464
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
#include <sqlite3.h>
#include "common.h"
/* persist on disk with compact representation
date representation -> value
*/
void store_result(U32 val, struct date d);
void retrieve_result(U32 val, struct date d);
void retrieve_results_before(int *vals, struct date before);
+28
View File
@@ -0,0 +1,28 @@
#ifndef WINDOWSTATE_H
#define WINDOWSTATE_H
#include "common.h"
#include <stddef.h>
#include <time.h>
enum mode_t {
WINDOW = 0,
OVERLAY
};
typedef enum mode_t Mode;
void init_window_state();
void cleanup_window_state();
void set_intention(char *intention);
char *get_intention();
size_t get_intention_size();
Mode get_mode();
void set_mode(Mode mode);
// returns 1 for win, 0 for loss
U32 get_result(struct tm *time);
void set_result(struct tm *time, U32 result);
#endif
-16
View File
@@ -1,16 +0,0 @@
#include <iostream>
#include <QApplication>
#include "mainwindow.h"
#include "service.h"
int main(int argc, char *argv[]) {
std::cout << "Starting adhd program" << std::endl;
QApplication a(argc, argv);
a.setStyle("fusion");
MainWindow w;
w.show();
return a.exec();
}
-264
View File
@@ -1,264 +0,0 @@
#include "mainwindow.h"
#include "service.h"
#include "sessionslistmodel.h"
#include "sessionitemdelegate.h"
#include "createsessiondialog.h"
#include "focusoverlay.h"
#include "endsessiondialog.h"
#include <QFileDialog>
#include <QLabel>
#include <QVBoxLayout>
#include <QTimer>
#include <QMessageBox>
#include <QSettings>
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
m_service(new Service(this)),
m_focusOverlay(nullptr),
m_updatingActiveFields(false)
{
ui = new Ui::MainWindow;
ui->setupUi(this);
setWindowTitle("flowy.adhd");
m_listModel = new SessionsListModel(m_service, this);
ui->listView->setModel(m_listModel);
ui->listView->setItemDelegate(new SessionItemDelegate(this));
// Connect Service signals
connect(m_service, &Service::activeSessionChanged,
this, &MainWindow::onActiveSessionChanged);
connect(m_service, &Service::sessionTimerTicked,
this, &MainWindow::onSessionTimerTicked);
// Connect Active tab field changes
connect(ui->oneLineEdit, &QLineEdit::editingFinished,
this, &MainWindow::onActiveOnelinerChanged);
// Debounce content text changes
QTimer *contentDebounce = new QTimer(this);
contentDebounce->setSingleShot(true);
contentDebounce->setInterval(500);
connect(ui->contentTextEdit, &QTextEdit::textChanged,
[contentDebounce]() { contentDebounce->start(); });
connect(contentDebounce, &QTimer::timeout,
this, &MainWindow::onActiveContentChanged);
// Set initial state: no active session
ui->activeStackedWidget->setCurrentIndex(0);
// Load saved vault directory from settings
QSettings settings("flowy", "adhd");
QString savedVaultPath = settings.value("vaultDirectory").toString();
if (!savedVaultPath.isEmpty() && QDir(savedVaultPath).exists()) {
m_service->setVaultDirectory(savedVaultPath);
ui->vaultLabel->setText(savedVaultPath);
// Switch to Sessions tab if we have a valid vault
ui->tabWidget->setCurrentIndex(1);
}
}
MainWindow::~MainWindow() {}
void MainWindow::on_selectVaultButton_clicked()
{
QString dir = QFileDialog::getExistingDirectory(this,
"Select Directory",
QDir::homePath(),
QFileDialog::ShowDirsOnly);
if (!dir.isEmpty()) {
m_service->setVaultDirectory(dir);
ui->tabWidget->setCurrentIndex(1);
ui->vaultLabel->setText(dir);
// Save to settings for next launch
QSettings settings("flowy", "adhd");
settings.setValue("vaultDirectory", dir);
}
}
void MainWindow::on_newSessionButton_clicked()
{
CreateSessionDialog dialog(this);
if (dialog.exec() == QDialog::Accepted) {
QString oneliner = dialog.getOneliner();
QString content = dialog.getContent();
Session session = m_service->createSession(oneliner, content);
m_service->startSession(session);
}
}
void MainWindow::on_startSessionButton_clicked()
{
QModelIndex index = ui->listView->currentIndex();
if (!index.isValid()) {
QMessageBox::warning(this, "No Selection", "Please select a session to start.");
return;
}
Session session = m_listModel->getSessionByIndex(index);
m_service->startSession(session);
}
void MainWindow::on_endSessionButton_clicked()
{
auto activeSession = m_service->activeSession();
if (!activeSession) {
return;
}
EndSessionDialog dialog(activeSession->oneliner, m_service->getElapsedMs(), this);
if (dialog.exec() == QDialog::Accepted) {
Session::Result result = dialog.getResult();
QString notes = dialog.getAdditionalNotes();
// Append notes to content if provided
if (!notes.isEmpty()) {
Session updated = *activeSession;
if (!updated.content.isEmpty()) {
updated.content += "\n\n";
}
updated.content += notes;
m_service->updateSession(updated);
}
m_service->endSession(result);
}
}
void MainWindow::onActiveSessionChanged()
{
auto activeSession = m_service->activeSession();
if (activeSession) {
// Session started
ui->activeStackedWidget->setCurrentIndex(1);
updateActiveTab();
// Create and show overlay
if (!m_focusOverlay) {
m_focusOverlay = new FocusOverlay();
connect(m_focusOverlay, &FocusOverlay::resetClicked,
this, &MainWindow::onOverlayResetClicked);
connect(m_focusOverlay, &FocusOverlay::endSessionClicked,
this, &MainWindow::onOverlayEndSessionClicked);
}
m_focusOverlay->setOneliner(activeSession->oneliner);
m_focusOverlay->setElapsedTime(0);
m_focusOverlay->show();
// Switch to Active tab
ui->tabWidget->setCurrentIndex(2);
} else {
// Session ended
ui->activeStackedWidget->setCurrentIndex(0);
// Hide and delete overlay
if (m_focusOverlay) {
m_focusOverlay->hide();
m_focusOverlay->deleteLater();
m_focusOverlay = nullptr;
}
}
}
void MainWindow::onSessionTimerTicked(int elapsedMs)
{
ui->elapsedLabel->setText(formatDuration(elapsedMs));
if (m_focusOverlay) {
m_focusOverlay->setElapsedTime(elapsedMs);
}
}
void MainWindow::onActiveOnelinerChanged()
{
if (m_updatingActiveFields) return;
auto activeSession = m_service->activeSession();
if (!activeSession) return;
Session updated = *activeSession;
updated.oneliner = ui->oneLineEdit->text();
updated.lastModifiedAt = QDateTime::currentDateTime();
m_updatingActiveFields = true;
m_service->updateSession(updated);
m_service->updateActiveSession(updated);
// Update overlay
if (m_focusOverlay) {
m_focusOverlay->setOneliner(updated.oneliner);
}
m_updatingActiveFields = false;
}
void MainWindow::onActiveContentChanged()
{
if (m_updatingActiveFields) return;
auto activeSession = m_service->activeSession();
if (!activeSession) return;
Session updated = *activeSession;
updated.content = ui->contentTextEdit->toPlainText();
updated.lastModifiedAt = QDateTime::currentDateTime();
m_updatingActiveFields = true;
m_service->updateSession(updated);
m_service->updateActiveSession(updated);
m_updatingActiveFields = false;
}
void MainWindow::onOverlayResetClicked()
{
QMessageBox::StandardButton reply = QMessageBox::question(
this,
"Reset Timer",
"Are you sure you want to reset the timer to 00:00:00?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
m_service->resetSessionTimer();
}
}
void MainWindow::onOverlayEndSessionClicked()
{
on_endSessionButton_clicked();
}
void MainWindow::updateActiveTab()
{
auto activeSession = m_service->activeSession();
if (!activeSession) return;
m_updatingActiveFields = true;
ui->oneLineEdit->setText(activeSession->oneliner);
ui->elapsedLabel->setText(formatDuration(m_service->getElapsedMs()));
ui->createdLabel->setText(activeSession->createdAt.toString("yyyy-MM-dd hh:mm:ss"));
ui->modifiedLabel->setText(activeSession->lastModifiedAt.toString("yyyy-MM-dd hh:mm:ss"));
ui->contentTextEdit->setPlainText(activeSession->content);
m_updatingActiveFields = false;
}
QString MainWindow::formatDuration(int ms) const
{
int seconds = ms / 1000;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
return QString("%1:%2:%3")
.arg(hours, 2, 10, QChar('0'))
.arg(minutes, 2, 10, QChar('0'))
.arg(secs, 2, 10, QChar('0'));
}
-56
View File
@@ -1,56 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QObject>
#include <QMainWindow>
class Service;
class SessionsListModel;
class FocusOverlay;
class QTimer;
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_selectVaultButton_clicked();
void on_newSessionButton_clicked();
void on_startSessionButton_clicked();
void on_endSessionButton_clicked();
// Service signal handlers
void onActiveSessionChanged();
void onSessionTimerTicked(int elapsedMs);
// Active tab field editing
void onActiveOnelinerChanged();
void onActiveContentChanged();
// Overlay handlers
void onOverlayResetClicked();
void onOverlayEndSessionClicked();
private:
Ui::MainWindow *ui;
Service *m_service;
SessionsListModel *m_listModel;
FocusOverlay *m_focusOverlay;
bool m_updatingActiveFields;
void updateActiveTab();
QString formatDuration(int ms) const;
};
#endif // MAINWINDOW_H
-289
View File
@@ -1,289 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Settings</string>
</attribute>
<widget class="QLabel" name="vaultLabel">
<property name="geometry">
<rect>
<x>80</x>
<y>210</y>
<width>221</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Select a folder</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="selectVaultButton">
<property name="geometry">
<rect>
<x>130</x>
<y>240</y>
<width>121</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>Select vault</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentOpen"/>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Sessions</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="newSessionButton">
<property name="text">
<string>New Session</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startSessionButton">
<property name="text">
<string>Start Selected</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="listView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Active</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QStackedWidget" name="activeStackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="noSessionPage">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="noSessionLabel">
<property name="text">
<string>No session running. Start a session to begin.</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="activeSessionPage">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>190</width>
<height>244</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Oneliner:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="oneLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Elapsed Time:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="elapsedLabel">
<property name="text">
<string>00:00:00</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="createdLabel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Modified:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="modifiedLabel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Content:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QTextEdit" name="contentTextEdit">
<property name="placeholderText">
<string>Add notes, context, or anything related to this session...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="endSessionButton">
<property name="text">
<string>End Session</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>30</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
-5
View File
@@ -1,5 +0,0 @@
#include "namegenerator.h"
QString generate() {
return QString("generated-name-placeholder");
}
-8
View File
@@ -1,8 +0,0 @@
#ifndef NAMEGENERATOR_H
#define NAMEGENERATOR_H
#include <QString>
QString generate();
#endif //NAMEGENERATOR_H
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash -e
BUILD_DIR=build
if [ ! -d "$BUILD_DIR" ]; then
cmake -B$BUILD_DIR -S. -GNinja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-Wall -Wextra" \
-DCMAKE_PREFIX_PATH="$HOME/Qt/6.8.6/macos" \
-DCMAKE_COLOR_DIAGNOSTICS=ON \
-DCMAKE_CXX_FLAGS_INIT="-DQT_QML_DEBUG"
fi
cmake --build $BUILD_DIR --target all
$BUILD_DIR/adhd.app/Contents/MacOS/adhd
-349
View File
@@ -1,349 +0,0 @@
#include "service.h"
#include <QDir>
#include <QFileInfoList>
#include <QUuid>
#include <QFile>
bool Session::isComplete() const {
return result != UNSPECIFIED;
}
Service::Service(QObject *parent)
: QObject(parent),
m_tickTimer(new QTimer(this))
{
m_tickTimer->setInterval(1000); // Fire every second
connect(m_tickTimer, &QTimer::timeout, this, &Service::onTickTimerFired);
}
Service::~Service() {}
QString Service::vaultDirectory() const {
return m_vaultDirectory;
}
int Service::sessionCount() const {
return m_sessions.size();
}
int Service::completedSessionCount() const {
int count = 0;
for (const Session &session : m_sessions) {
if (session.isComplete()) {
count++;
}
}
return count;
}
int Service::totalWins() const {
int count = 0;
for (const Session &session : m_sessions) {
if (session.result == Session::WIN) {
count++;
}
}
return count;
}
int Service::totalLosses() const {
int count = 0;
for (const Session &session : m_sessions) {
if (session.result == Session::LOSS) {
count++;
}
}
return count;
}
int Service::totalNeutrals() const {
int count = 0;
for (const Session &session : m_sessions) {
if (session.result == Session::NEUTRAL) {
count++;
}
}
return count;
}
QList<Session> Service::sessions() const {
return m_sessions;
}
void Service::setVaultDirectory(const QString &path) {
m_sessions.clear();
qDebug() << "Setting vault directory to: " << path;
QDir dir = QDir(path);
if (!dir.exists()) {
qWarning() << "Vault directory does not exist";
return;
}
m_vaultDirectory = path;
dir.setFilter(QDir::Files);
dir.setSorting(QDir::Time);
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
if (fileInfo.completeSuffix() != QStringLiteral("md")) {
qWarning() << "Skipping invalid file format: " << fileInfo.completeSuffix();
continue;
}
qDebug() << "Parsing file: " << fileInfo.absoluteFilePath();
std::optional<Session> session = parseFile(fileInfo);
if (session) {
m_sessions.append(*session);
} else {
qWarning() << "Failed to parse session file: " << fileInfo.absoluteFilePath();
}
}
emit sessionsChanged();
}
Session Service::createSession(const QString &oneliner, const QString &content) {
assert(!m_vaultDirectory.isEmpty() && "Vault directory must be set before creating sessions");
QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
Session session = {};
session.id = uuid;
session.oneliner = oneliner;
session.durationMs = 0;
session.content = content;
session.result = Session::UNSPECIFIED;
session.createdAt = QDateTime::currentDateTime();
session.lastModifiedAt = QDateTime::currentDateTime();
overwriteSessionFile(session);
m_sessions.prepend(session);
emit sessionsChanged();
return session;
}
void Service::updateSession(const Session &session) {
overwriteSessionFile(session);
for (int i = 0; i < m_sessions.size(); ++i) {
if (m_sessions[i].id == session.id) {
m_sessions[i] = session;
break;
}
}
emit sessionsChanged();
}
void Service::deleteSession(const QString &sessionId) {
QDir dir = QDir(m_vaultDirectory);
QString filePath = dir.filePath(sessionId + ".md");
QFile file = QFile(filePath);
if (!file.remove()) {
qWarning() << "Failed to delete session file: " << filePath;
return;
}
for (int i = 0; i < m_sessions.size(); ++i) {
if (m_sessions[i].id == sessionId) {
m_sessions.removeAt(i);
break;
}
}
emit sessionsChanged();
}
std::optional<Session> Service::parseFile(const QFileInfo &fileInfo) {
QString path = fileInfo.absoluteFilePath();
QFile file = QFile(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return std::nullopt;
}
Session session = {};
session.id = fileInfo.baseName(); // Get filename without extension
session.content = "";
session.oneliner = "";
session.durationMs = 0;
session.result = Session::UNSPECIFIED;
session.createdAt = fileInfo.birthTime();
session.lastModifiedAt = fileInfo.lastModified();
QTextStream in(&file);
int separatorCounts = 0;
while (!in.atEnd()) {
QString line = in.readLine();
// qDebug() << "Reading line: " << line;
if (separatorCounts >= 2) {
session.content += line + '\n';
continue;
}
switch (line[0].toLatin1()) {
case '-':
separatorCounts++;
continue;
break;
default:
break;
}
// parse each key value pair
QStringList parts = line.split(':');
if (parts.size() != 2) {
continue;
}
QString key = parts[0].trimmed();
QString value = parts[1].trimmed();
if (key == "Oneliner") {
session.oneliner = value;
continue;
} else if (key == "DurationMs") {
session.durationMs = value.toInt();
continue;
} else if (key == "Result") {
session.result = parseResult(value);
continue;
}
}
qDebug() << "Parsed oneliner: " << session.oneliner;
qDebug() << "Parsed durationMs: " << session.durationMs;
qDebug() << "Parsed result: " << static_cast<int>(session.result);
qDebug() << "Parsed content: " << session.content;
qDebug() << "Created at: " << session.createdAt;
qDebug() << "Last modified at: " << session.lastModifiedAt;
file.close();
return session;
}
Session::Result Service::parseResult(const QString &resultStr) {
if (resultStr == "Win") {
return Session::WIN;
} else if (resultStr == "Loss") {
return Session::LOSS;
} else if (resultStr == "Neutral") {
return Session::NEUTRAL;
} else {
return Session::UNSPECIFIED;
}
}
void Service::overwriteSessionFile(const Session &session)
{
// rewrites the entire file
QDir dir = QDir(m_vaultDirectory);
QString filePath = dir.filePath(session.id + ".md");
QFile file = QFile(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Failed to update session file: " << filePath;
return;
}
QTextStream out(&file);
out << "---\n";
out << "Oneliner: " << session.oneliner << "\n";
out << "DurationMs: " << session.durationMs << "\n";
QString resultStr;
switch (session.result) {
case Session::WIN:
resultStr = "Win";
break;
case Session::LOSS:
resultStr = "Loss";
break;
case Session::NEUTRAL:
resultStr = "Neutral";
break;
default:
resultStr = "";
break;
}
if (!resultStr.isEmpty()) {
out << "Result: " << resultStr << "\n";
}
out << "---\n";
out << session.content << "\n";
file.close();
}
// Session lifecycle methods
void Service::startSession(const Session &session) {
if (m_activeSession.has_value()) {
qWarning() << "A session is already active. End it before starting a new one.";
return;
}
m_activeSession = session;
m_sessionTimer.start();
m_tickTimer->start();
emit activeSessionChanged();
emit sessionTimerTicked(0);
}
void Service::updateActiveSession(const Session &session) {
if (!m_activeSession.has_value()) {
qWarning() << "No active session to update.";
return;
}
m_activeSession = session;
// Don't emit activeSessionChanged - just update the session data
// Timer keeps running
}
int Service::getElapsedMs() const {
if (!m_activeSession) {
return 0;
}
return m_sessionTimer.elapsed();
}
void Service::resetSessionTimer() {
if (!m_activeSession) {
return;
}
m_sessionTimer.restart();
emit sessionTimerTicked(0);
}
void Service::endSession(Session::Result result) {
if (!m_activeSession) {
return;
}
// Update session with final values
Session updated = *m_activeSession;
updated.durationMs = getElapsedMs();
updated.result = result;
updated.lastModifiedAt = QDateTime::currentDateTime();
// Save to file and update in list
updateSession(updated);
// Clear active state
m_activeSession.reset();
m_tickTimer->stop();
emit activeSessionChanged();
}
std::optional<Session> Service::activeSession() const {
return m_activeSession;
}
void Service::onTickTimerFired() {
if (m_activeSession) {
emit sessionTimerTicked(getElapsedMs());
}
}
-104
View File
@@ -1,104 +0,0 @@
#ifndef SERVICE_H
#define SERVICE_H
#include <QList>
#include <QObject>
#include <QDateTime>
#include <QString>
#include <QFileInfo>
#include <QElapsedTimer>
#include <QTimer>
#include <optional>
struct Session
{
enum Result {
UNSPECIFIED = 0,
WIN,
LOSS,
NEUTRAL
};
// Becomes the file name
QString id;
// Stored in the frontmatter under 'Oneliner' key
QString oneliner;
// Stored in the frontmatter
int durationMs;
// After the --- onwards, serves as general scratchpad
QString content;
// Stored in the frontmatter under 'Result' key
Result result;
QDateTime lastModifiedAt;
QDateTime createdAt;
bool isComplete() const;
};
class Service : public QObject {
Q_OBJECT
public:
explicit Service(QObject *parent = nullptr);
~Service();
QList<Session> sessions() const;
QString vaultDirectory() const;
int sessionCount() const;
int completedSessionCount() const;
int totalWins() const;
int totalLosses() const;
int totalNeutrals() const;
public slots:
/// @brief sets the directory from which to read/write sessions
/// Fails silently if not a valid path
/// Loads sessions from this path
void setVaultDirectory(const QString &path);
/// @brief creates the session and stores in sessions list
/// @returns the newly created session
Session createSession(const QString &oneliner, const QString &content);
/// @brief updates the session
/// Updates session in the sessions list, and the file associated
void updateSession(const Session &session);
void deleteSession(const QString &sessionId);
// Session lifecycle methods
void startSession(const Session &session);
void updateActiveSession(const Session &session); // Update without restarting timer
int getElapsedMs() const;
void resetSessionTimer();
void endSession(Session::Result result);
std::optional<Session> activeSession() const;
signals:
void sessionsChanged();
void activeSessionChanged();
void sessionTimerTicked(int elapsedMs);
private:
QString m_vaultDirectory;
QList<Session> m_sessions;
// Active session tracking
std::optional<Session> m_activeSession;
QElapsedTimer m_sessionTimer;
QTimer *m_tickTimer;
/// @returns null Session if not a valid session markdown document
std::optional<Session> parseFile(const QFileInfo &fileInfo);
Session::Result parseResult(const QString &resultStr);
void overwriteSessionFile(const Session &session);
private slots:
void onTickTimerFired();
};
Q_DECLARE_METATYPE(Session)
#endif //SERVICE_H
-139
View File
@@ -1,139 +0,0 @@
#include "sessionitemdelegate.h"
#include "service.h"
#include <QPainter>
#include <QDateTime>
SessionItemDelegate::SessionItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
void SessionItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (!index.isValid()) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
painter->save();
// Get session data
Session session = index.data(Qt::UserRole).value<Session>();
QString oneliner = session.oneliner;
// Format duration
int seconds = session.durationMs / 1000;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
QString durationStr = QString("%1:%2:%3")
.arg(hours, 2, 10, QChar('0'))
.arg(minutes, 2, 10, QChar('0'))
.arg(secs, 2, 10, QChar('0'));
// Format result
QString resultStr;
switch (session.result) {
case Session::WIN:
resultStr = "WIN";
break;
case Session::LOSS:
resultStr = "LOSS";
break;
case Session::NEUTRAL:
resultStr = "NEUTRAL";
break;
default:
resultStr = "UNSPECIFIED";
break;
}
// Format relative time
QDateTime now = QDateTime::currentDateTime();
qint64 secsSinceModified = session.lastModifiedAt.secsTo(now);
QString relativeTime;
if (secsSinceModified < 60) {
relativeTime = "just now";
} else if (secsSinceModified < 3600) {
relativeTime = QString("%1 min ago").arg(secsSinceModified / 60);
} else if (secsSinceModified < 86400) {
relativeTime = QString("%1 hr ago").arg(secsSinceModified / 3600);
} else {
relativeTime = QString("%1 days ago").arg(secsSinceModified / 86400);
}
QString metadata = QString("Duration: %1 | Result: %2 | Modified: %3")
.arg(durationStr)
.arg(resultStr)
.arg(relativeTime);
// Draw background
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
} else {
painter->fillRect(option.rect, option.palette.base());
}
// Draw result indicator bar on the left
QColor indicatorColor;
switch (session.result) {
case Session::WIN:
indicatorColor = QColor(46, 204, 113); // Green
break;
case Session::LOSS:
indicatorColor = QColor(231, 76, 60); // Red
break;
case Session::NEUTRAL:
indicatorColor = QColor(149, 165, 166); // Gray
break;
default:
indicatorColor = QColor(0, 0, 0, 0); // Transparent
break;
}
if (session.result != Session::UNSPECIFIED) {
QRect indicatorRect(option.rect.left(), option.rect.top(), 4, option.rect.height());
painter->fillRect(indicatorRect, indicatorColor);
}
// Set up text drawing
QRect textRect = option.rect.adjusted(10, 5, -5, -5);
QColor textColor = (option.state & QStyle::State_Selected)
? option.palette.highlightedText().color()
: option.palette.text().color();
// Draw oneliner (bold, larger font)
painter->setPen(textColor);
QFont onelineFont = option.font;
onelineFont.setPointSize(14);
onelineFont.setBold(true);
painter->setFont(onelineFont);
QRect onelineRect = textRect;
onelineRect.setHeight(24);
QString elidedOneliner = painter->fontMetrics().elidedText(oneliner, Qt::ElideRight, onelineRect.width());
painter->drawText(onelineRect, Qt::AlignLeft | Qt::AlignVCenter, elidedOneliner);
// Draw metadata (smaller font, lighter color)
QFont metadataFont = option.font;
metadataFont.setPointSize(11);
painter->setFont(metadataFont);
QColor metadataColor = textColor;
metadataColor.setAlpha(180); // Make it slightly transparent
painter->setPen(metadataColor);
QRect metadataRect = textRect;
metadataRect.setTop(onelineRect.bottom() + 2);
QString elidedMetadata = painter->fontMetrics().elidedText(metadata, Qt::ElideRight, metadataRect.width());
painter->drawText(metadataRect, Qt::AlignLeft | Qt::AlignVCenter, elidedMetadata);
painter->restore();
}
QSize SessionItemDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
return QSize(option.rect.width(), 65);
}
-20
View File
@@ -1,20 +0,0 @@
#ifndef SESSIONITEMDELEGATE_H
#define SESSIONITEMDELEGATE_H
#include <QStyledItemDelegate>
class SessionItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit SessionItemDelegate(QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
#endif // SESSIONITEMDELEGATE_H
-45
View File
@@ -1,45 +0,0 @@
#include "sessionslistmodel.h"
SessionsListModel::SessionsListModel(Service *service, QObject *parent)
: QAbstractListModel{parent}, m_service(service)
{
connect(m_service, &Service::sessionsChanged, this, [this]() {
beginResetModel();
endResetModel();
});
}
int SessionsListModel::rowCount(const QModelIndex &parent) const {
return m_service->sessions().size();
}
int SessionsListModel::columnCount(const QModelIndex &parent) const {
return 1;
}
QVariant SessionsListModel::data(const QModelIndex &index, int role) const {
if (index.row() < 0 || index.row() >= rowCount()) {
return QVariant();
} else if (!index.isValid()) {
return QVariant();
}
const Session session = m_service->sessions().at(index.row());
switch (role) {
case Qt::DisplayRole:
// Simple display for fallback - delegate will handle rich rendering
return session.oneliner;
case Qt::UserRole:
// Store full session object for delegate access
return QVariant::fromValue(session);
default:
return QVariant();
}
}
Session SessionsListModel::getSessionByIndex(const QModelIndex &index) const {
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return Session(); // Return default-constructed session
}
return m_service->sessions().at(index.row());
}
-24
View File
@@ -1,24 +0,0 @@
#ifndef SESSIONSLISTMODEL_H
#define SESSIONSLISTMODEL_H
#include <QAbstractListModel>
#include <QObject>
#include "service.h"
class SessionsListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit SessionsListModel(Service *service, QObject *parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
Session getSessionByIndex(const QModelIndex &index) const;
private:
Service *m_service;
};
#endif // SESSIONSLISTMODEL_H
+182
View File
@@ -0,0 +1,182 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#include <raylib.h>
#include "window_state.h"
#include "common.h"
#define WINDOW_HEIGHT 700
#define WINDOW_WIDTH 550
#define OVERLAY_HEIGHT 400
#define OVERLAY_WIDTH 700
#define NUM_WEEKS 12
void switch_mode(Mode mode) {
set_mode(mode);
switch (mode) {
case WINDOW:
SetWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
break;
case OVERLAY:
SetWindowSize(OVERLAY_WIDTH, OVERLAY_HEIGHT);
break;
default:
break;
}
}
typedef struct cell_t {
int row;
int column;
} Cell;
int cell_number(Cell c) {
return c.row + 1 + (c.column * 7);
}
int distance(Cell a, Cell b) {
return cell_number(b) - cell_number(a);
}
#define CELL_SPACING 10
#define CELL_SIZE 20
time_t now;
struct tm local_time;
// Returns the time info struct which is a static variable within time.h stdlib
struct tm *get_date_days_ago(int days_ago, char *buffer, size_t buffer_size) {
time_t raw_time;
struct tm *time_info;
time(&raw_time);
time_info = localtime(&raw_time); // caution: localtime() overwrites the static struct internally as opposed to localtime_r
time_info->tm_mday -= days_ago;
mktime(time_info);
strftime(buffer, buffer_size, "%B %d", time_info);
return time_info;
}
void draw_graph(int x, int y) {
GuiEnableTooltip();
int num_columns = NUM_WEEKS;
int num_rows = 7; // days in a week
int cell_number_today = cell_number((Cell) {local_time.tm_wday, num_columns - 1});
for (int c = 0; c < num_columns; c++) {
for (int r = 0; r < num_rows; r++) {
Cell cell = { r, c };
int curr_cell_number = cell_number(cell);
if (curr_cell_number > cell_number_today) break;
int days_ago = cell_number_today - curr_cell_number;
char tooltip_text[128];
struct tm *time = get_date_days_ago(days_ago, tooltip_text, sizeof(tooltip_text));
U32 result = get_result(time);
GuiSetTooltip(tooltip_text);
// full size of a cell is spacing * 2 + size, and then need to go little more
int cell_x = CELL_SPACING + (((CELL_SPACING * 2) + CELL_SIZE) * c);
int cell_y = CELL_SPACING + (((CELL_SPACING * 2) + CELL_SIZE) * r);
if (result) {
GuiSetStyle(BUTTON, BASE_COLOR_NORMAL, ColorToInt(GREEN));
} else {
GuiSetStyle(BUTTON, BASE_COLOR_NORMAL, ColorToInt(BLANK));
}
if (GuiButton((Rectangle) {cell_x + x, cell_y + y, CELL_SIZE, CELL_SIZE}, " ")) {
set_result(time, abs(result - 1));
}
if (c == 0 && r == 1) {
GuiLabel((Rectangle) {x-5, cell_y + y, 20, 20}, "M");
} else if (c==0 && r== 3) {
GuiLabel((Rectangle) {x-5, cell_y + y, 20, 20}, "W");
} else if (c==0 && r == 5) {
GuiLabel((Rectangle) {x-5, cell_y + y, 20, 20}, "F");
}
}
}
GuiDisableTooltip();
}
bool edit_mode = false;
void render_window() {
ClearBackground(RAYWHITE);
DrawFPS(WINDOW_WIDTH - 50, 10);
Rectangle text_box_rect = {(WINDOW_WIDTH / 2) - 200, (WINDOW_HEIGHT / 2) - 200, 400, 50};
Vector2 mouse_pos = GetMousePosition();
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
if (CheckCollisionRecs((Rectangle) {mouse_pos.x, mouse_pos.y, 2, 2}, text_box_rect)) {
edit_mode = true;
} else {
edit_mode = false;
}
}
if (GuiTextBox(text_box_rect, get_intention(), get_intention_size(), edit_mode)) {
switch_mode(OVERLAY);
}
char label[2048];
sprintf(label, "%d / %d characters | Hit enter to set", (int) strlen(get_intention()), (int) get_intention_size());
GuiLabel((Rectangle){ WINDOW_WIDTH / 2 - 100, (WINDOW_HEIGHT / 2) - 150, 300, 50}, label);
int graph_y_offset = (WINDOW_HEIGHT / 2) + 50;
draw_graph(20, graph_y_offset);
}
void render_overlay() {
ClearBackground(BLANK);
DrawFPS(OVERLAY_WIDTH - 50, 10);
const Rectangle button_rect = {OVERLAY_WIDTH - 100, 10, 100, 25};
int active = 0;
if (GuiToggleSlider(button_rect, "Toggle mode", &active)) {
switch_mode(WINDOW);
}
GuiSetStyle(DEFAULT, TEXT_SIZE, 75);
GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_CHAR);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
GuiLabel((Rectangle){0, 0, OVERLAY_WIDTH, OVERLAY_HEIGHT}, get_intention());
GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, 0);
}
int main(int argc, char *argv[]) {
time(&now);
localtime_r(&now, &local_time);
mktime(&local_time);
init_window_state();
printf("Welcome to Flowy.adhd\n");
InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Flowy.adhd");
while (!WindowShouldClose()) {
BeginDrawing();
GuiSetStyle(DEFAULT, TEXT_SIZE, 15);
if (get_mode() == WINDOW) render_window();
else render_overlay();
EndDrawing();
}
CloseWindow();
return 0;
}
+231
View File
@@ -0,0 +1,231 @@
#include "window_state.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sqlite3.h>
struct state *s = NULL;
#define INTENTION_MAX_SIZE 2048
typedef struct record_t {
char *key;
U32 value;
} Record;
struct state {
Mode mode;
char intention[INTENTION_MAX_SIZE];
Record **records; // array of records
size_t size_records;
size_t capacity_records;
};
static sqlite3 *db = NULL;
static void db_open() {
int r = sqlite3_open("local.sqlite3", &db);
if (r == SQLITE_OK) {
} else {
fprintf(stderr, "Unable to prepare sqlite db\n");
exit(-1);
}
const char *sql =
"CREATE TABLE IF NOT EXISTS records ("
"key TEXT PRIMARY KEY,"
"value INT"
");"
;
char *err;
r = sqlite3_exec(db, sql, NULL, NULL, &err);
if (r != SQLITE_OK) {
fprintf(stderr, "SQL error during setup: %s\n", err);
sqlite3_free(err);
sqlite3_close(db);
exit(-1);
}
}
static void db_close() {
if (db) {
sqlite3_close(db);
}
}
static int db_get_record_callback(void *ctx, int argc, char *argv[], char **argv_col_name) {
Record *rec = calloc(1, sizeof(Record));
for (int i = 0; i < argc; i++) {
if (strcmp(argv_col_name[i], "key") == 0) {
rec->key = strdup(argv[i]);
} else if (strcmp(argv_col_name[i], "value") == 0) {
rec->value = (U32) atoi(argv[i]);
}
}
if (s->size_records >= s->capacity_records) {
printf("MAX capacity");
return 0;
}
*(s->records + s->size_records) = rec;
s->size_records++;
return 0;
}
static void db_upsert_record(char *key, U32 value) {
sqlite3_stmt *stmt;
const char *sql = "INSERT INTO records (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = ?;";
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
assert(rc == SQLITE_OK);
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, value);
sqlite3_bind_int(stmt, 3, value);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
printf("Failure in insert: %s\n", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
}
static void db_get_all_records() {
const char *sql = "SELECT key, value FROM records;";
int rc = sqlite3_exec(db, sql, db_get_record_callback, NULL, NULL);
assert(rc == SQLITE_OK);
}
void init_window_state() {
s = malloc(sizeof(struct state));
s->mode = WINDOW;
memset(s->intention, 0, sizeof(s->intention));
s->size_records = 0;
s->capacity_records = 1000; //TODO: make dynamic
s->records = malloc(sizeof(Record *) * s->capacity_records);
db_open();
db_get_all_records();
}
static Record *append_record(char *key, U32 value) {
if (s->size_records >= s->capacity_records) {
printf("MAX capacity");
return NULL;
}
char *cpy = strdup(key);
Record *new_record = malloc(sizeof(Record));
new_record->key = cpy;
new_record->value = value;
*(s->records + s->size_records) = new_record;
s->size_records++;
return new_record;
}
static void upsert_record(char *key, U32 value) {
bool found = false;
for (int i = 0; i < s->size_records; i++) {
Record *rec = *(s->records + i);
if (strcmp(rec->key, key) == 0) {
found = true;
rec->value = value;
break;
}
}
if (!found) {
append_record(key, value);
}
}
static Record *get_record(char *key) {
for (int i = 0; i < s->size_records; i++) {
Record *rec = *(s->records + i);
if (strcmp(rec->key, key) == 0) {
return rec;
}
}
return NULL;
}
static void print_records() {
for (int i = 0; i < s->size_records; i++) {
Record *rec = *(s->records + i);
printf("record: %s -> %d\n", rec->key, rec->value);
}
}
static void generate_key(struct tm *time, char *buffer, size_t buffer_size) {
snprintf(buffer, buffer_size, "%d", time->tm_yday);
}
void cleanup_window_state() {
if (!s) {
return;
}
for (int i = 0; i < s->size_records; i++) {
Record *rec = *(s->records + s->size_records);
free(rec);
}
free(s->records);
free(s);
s = NULL;
db_close();
}
void set_intention(char *intention) {
assert(s);
strncpy(s->intention, intention, sizeof(s->intention));
s->intention[sizeof(s->intention) - 1] = '\0';
}
char *get_intention() {
assert(s);
return s->intention;
}
size_t get_intention_size() {
assert(s);
return sizeof(s->intention);
}
Mode get_mode() {
assert(s);
return s->mode;
}
void set_mode(Mode new_mode) {
s->mode = new_mode;
}
U32 get_result(struct tm *time) {
char key[10];
generate_key(time, key, sizeof(key));
Record *rec = get_record(key);
if (!rec) {
return 0;
}
return rec->value;
}
void set_result(struct tm *time, U32 result) {
char key[10];
generate_key(time, key, sizeof(key));
upsert_record(key, result);
db_upsert_record(key, result);
}