macOS-like Custom Start Page Full Version

A full version macOS-like start page with weather, to-do persistence, voice search, and responsive design

  1. // ==UserScript==
  2. // @name macOS-like Custom Start Page Full Version
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.1
  5. // @description A full version macOS-like start page with weather, to-do persistence, voice search, and responsive design
  6. // @author 文熙
  7. // @license MIT
  8. // @match *://*/blank.html
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Helper function to detect if it's mobile
  16. function isMobile() {
  17. return /Mobi|Android/i.test(navigator.userAgent);
  18. }
  19.  
  20. // Define custom HTML for macOS-style start page with advanced features
  21. const startPageHtml = `
  22. <div class="container">
  23. <div class="widget glass">
  24. <h1 class="title">Good Day</h1>
  25. <div class="time-widget" id="currentTime">--:--:--</div>
  26. <div class="search-section">
  27. <input type="text" id="searchBar" class="search-bar" placeholder="Search the web..." />
  28. <button id="voiceSearch" class="voice-search" title="Voice Search"><img src="https://upload.wikimedia.org/wikipedia/commons/e/e1/Microphone_Icon.svg" alt="Voice Search" width="24" height="24"></button>
  29. </div>
  30. <select id="searchEngine" class="search-engine">
  31. <option value="https://www.google.com/search?q=">Google</option>
  32. <option value="https://www.bing.com/search?q=">Bing</option>
  33. <option value="https://duckduckgo.com/?q=">DuckDuckGo</option>
  34. </select>
  35. </div>
  36. <div class="dock">
  37. <a href="https://www.google.com" target="_blank" class="dock-icon"><img src="https://upload.wikimedia.org/wikipedia/commons/2/2f/Google_2015_logo.svg" alt="Google" width="48" height="48"></a>
  38. <a href="https://www.github.com" target="_blank" class="dock-icon"><img src="https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg" alt="GitHub" width="48" height="48"></a>
  39. <a href="https://www.reddit.com" target="_blank" class="dock-icon"><img src="https://upload.wikimedia.org/wikipedia/en/8/82/Reddit_logo_and_wordmark.svg" alt="Reddit" width="48" height="48"></a>
  40. </div>
  41. <div class="widgets-container">
  42. <div class="calendar-widget glass">
  43. <div id="currentDate">--/--/----</div>
  44. </div>
  45. <div class="weather-widget glass">
  46. <h2 class="weather-title">Weather</h2>
  47. <div id="weatherInfo">Loading...</div>
  48. </div>
  49. <div class="todo-widget glass">
  50. <h2 class="todo-title">To-Do List</h2>
  51. <ul id="todoList" class="todo-list"></ul>
  52. <input type="text" id="todoInput" class="todo-input" placeholder="Add a new task..." />
  53. <button id="clearTodos" class="clear-button">Clear All</button>
  54. </div>
  55. </div>
  56. <div class="settings glass">
  57. <button id="themeButton" class="theme-button">Change Theme</button>
  58. </div>
  59. </div>
  60. `;
  61.  
  62. // Inject the HTML into the body
  63. document.body.innerHTML = startPageHtml;
  64. document.title = "macOS-like Start Page";
  65.  
  66. // Function to update the current time every second
  67. function updateTime() {
  68. const now = new Date();
  69. const hours = String(now.getHours()).padStart(2, '0');
  70. const minutes = String(now.getMinutes()).padStart(2, '0');
  71. const seconds = String(now.getSeconds()).padStart(2, '0');
  72. document.getElementById('currentTime').textContent = `${hours}:${minutes}:${seconds}`;
  73. }
  74. setInterval(updateTime, 1000);
  75. updateTime();
  76.  
  77. // Function to update the current date
  78. function updateDate() {
  79. const now = new Date();
  80. const day = String(now.getDate()).padStart(2, '0');
  81. const month = String(now.getMonth() + 1).padStart(2, '0');
  82. const year = now.getFullYear();
  83. document.getElementById('currentDate').textContent = `${day}/${month}/${year}`;
  84. }
  85. updateDate();
  86.  
  87. // Search bar functionality with engine selection
  88. const searchBar = document.getElementById('searchBar');
  89. const searchEngine = document.getElementById('searchEngine');
  90. searchBar.addEventListener('keypress', function(e) {
  91. if (e.key === 'Enter') {
  92. const query = searchBar.value.trim();
  93. if (query) {
  94. const engineUrl = searchEngine.value;
  95. window.location.href = `${engineUrl}${encodeURIComponent(query)}`;
  96. }
  97. }
  98. });
  99.  
  100. // Voice Search functionality
  101. const voiceSearchButton = document.getElementById('voiceSearch');
  102. voiceSearchButton.addEventListener('click', function() {
  103. const recognition = new webkitSpeechRecognition();
  104. recognition.lang = 'en-US';
  105. recognition.start();
  106. recognition.onresult = function(event) {
  107. const query = event.results[0][0].transcript;
  108. searchBar.value = query;
  109. const engineUrl = searchEngine.value;
  110. window.location.href = `${engineUrl}${encodeURIComponent(query)}`;
  111. };
  112. });
  113.  
  114. // To-Do List functionality with persistence
  115. const todoInput = document.getElementById('todoInput');
  116. const todoList = document.getElementById('todoList');
  117. const clearTodosButton = document.getElementById('clearTodos');
  118.  
  119. // Load saved tasks from localStorage
  120. const savedTodos = JSON.parse(localStorage.getItem('todos')) || [];
  121. savedTodos.forEach(task => {
  122. const listItem = createTodoItem(task);
  123. todoList.appendChild(listItem);
  124. });
  125.  
  126. todoInput.addEventListener('keypress', function(e) {
  127. if (e.key === 'Enter') {
  128. const task = todoInput.value.trim();
  129. if (task) {
  130. const listItem = createTodoItem(task);
  131. todoList.appendChild(listItem);
  132. saveTodoItem(task);
  133. todoInput.value = ''; // Clear input after adding task
  134. }
  135. }
  136. });
  137.  
  138. clearTodosButton.addEventListener('click', function() {
  139. todoList.innerHTML = '';
  140. localStorage.removeItem('todos');
  141. });
  142.  
  143. function createTodoItem(task) {
  144. const listItem = document.createElement('li');
  145. listItem.textContent = task;
  146. listItem.addEventListener('click', () => {
  147. listItem.remove();
  148. removeTodoItem(task);
  149. });
  150. return listItem;
  151. }
  152.  
  153. function saveTodoItem(task) {
  154. const todos = JSON.parse(localStorage.getItem('todos')) || [];
  155. todos.push(task);
  156. localStorage.setItem('todos', JSON.stringify(todos));
  157. }
  158.  
  159. function removeTodoItem(task) {
  160. const todos = JSON.parse(localStorage.getItem('todos')) || [];
  161. const updatedTodos = todos.filter(t => t !== task);
  162. localStorage.setItem('todos', JSON.stringify(updatedTodos));
  163. }
  164.  
  165. // Weather Widget
  166. async function fetchWeather() {
  167. if (navigator.geolocation) {
  168. navigator.geolocation.getCurrentPosition(async function(position) {
  169. const lat = position.coords.latitude;
  170. const lon = position.coords.longitude;
  171. const apiKey = 'YOUR_OPENWEATHER_API_KEY'; // Replace with your API key
  172. const weatherUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=metric&appid=${apiKey}`;
  173. const response = await fetch(weatherUrl);
  174. const data = await response.json();
  175. const weatherInfo = `${data.weather[0].description}, ${data.main.tempC`;
  176. document.getElementById('weatherInfo').textContent = weatherInfo;
  177. });
  178. } else {
  179. document.getElementById('weatherInfo').textContent = 'Geolocation not supported';
  180. }
  181. }
  182. fetchWeather();
  183.  
  184. // Theme toggle button
  185. const themeButton = document.getElementById('themeButton');
  186. const currentTheme = localStorage.getItem('theme') || 'light';
  187. document.body.classList.toggle('dark-theme', currentTheme === 'dark');
  188. themeButton.textContent = currentTheme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode';
  189.  
  190. themeButton.addEventListener('click', () => {
  191. const isDark = document.body.classList.toggle('dark-theme');
  192. localStorage.setItem('theme', isDark ? 'dark' : 'light');
  193. themeButton.textContent = isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode';
  194. });
  195.  
  196. // Additional custom styles
  197. const customCss = `
  198. body {
  199. margin: 0;
  200. padding: 0;
  201. background: linear-gradient(135deg, #f0f0f5, #d0d0e5);
  202. font-family: -apple-system, BlinkMacSystemFont, "San Francisco", "Helvetica Neue", sans-serif;
  203. display: flex;
  204. justify-content: center;
  205. align-items: center;
  206. height: 100vh;
  207. transition: background-color 0.5s;
  208. }
  209. body.dark-theme {
  210. background-color: #2c2c2c;
  211. color: #ffffff;
  212. }
  213. .container {
  214. display: flex;
  215. flex-direction: column;
  216. align-items: center;
  217. width: 90%;
  218. max-width: 1200px;
  219. }
  220. .widget, .glass {
  221. text-align: center;
  222. background: rgba(255, 255, 255, 0.25);
  223. backdrop-filter: blur(10px);
  224. padding: 2rem;
  225. border-radius: 20px;
  226. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  227. margin-bottom: 2rem;
  228. width: 100%;
  229. }
  230. .title {
  231. font-size: 2.5rem;
  232. color: #333;
  233. margin-bottom: 1rem;
  234. }
  235. .time-widget {
  236. font-size: 2rem;
  237. color: #555;
  238. margin-bottom: 1rem;
  239. }
  240. .search-bar {
  241. width: 80%;
  242. padding: 0.75rem;
  243. font-size: 1.2rem;
  244. border: none;
  245. border-radius: 20px;
  246. outline: none;
  247. text-align: center;
  248. background-color: rgba(255, 255, 255, 0.7);
  249. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  250. transition: background-color 0.3s ease, box-shadow 0.3s ease;
  251. margin-bottom: 1rem;
  252. }
  253. .search-bar:focus {
  254. background-color: rgba(255, 255, 255, 0.9);
  255. box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
  256. }
  257. .voice-search {
  258. font-size: 1.5rem;
  259. background-color: #007aff;
  260. color: #fff;
  261. border: none;
  262. border-radius: 50%;
  263. padding: 0.5rem;
  264. cursor: pointer;
  265. margin-bottom: 1rem;
  266. }
  267. .search-engine {
  268. padding: 0.5rem;
  269. border: none;
  270. border-radius: 10px;
  271. margin-bottom: 2rem;
  272. }
  273. .dock {
  274. display: flex;
  275. justify-content: center;
  276. gap: 2rem;
  277. }
  278. .dock-icon {
  279. font-size: 1.2rem;
  280. color: #fff;
  281. background-color: rgba(0, 0, 0, 0.6);
  282. padding: 0.75rem 1.5rem;
  283. border-radius: 15px;
  284. text-decoration: none;
  285. transition: background-color 0.3s ease, transform 0.3s ease;
  286. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  287. }
  288. .dock-icon:hover {
  289. background-color: rgba(0, 0, 0, 0.8);
  290. transform: translateY(-5px);
  291. }
  292. .widgets-container {
  293. display: flex;
  294. flex-direction: column;
  295. justify-content: space-around;
  296. gap: 2rem;
  297. width: 100%;
  298. }
  299. .calendar-widget, .weather-widget, .todo-widget {
  300. padding: 1.5rem;
  301. background: rgba(255, 255, 255, 0.3);
  302. border-radius: 15px;
  303. backdrop-filter: blur(10px);
  304. box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
  305. }
  306. .calendar-widget #currentDate {
  307. font-size: 1.5rem;
  308. color: #333;
  309. }
  310. .weather-title, .todo-title {
  311. font-size: 1.5rem;
  312. color: #333;
  313. margin-bottom: 1rem;
  314. }
  315. .todo-list {
  316. list-style: none;
  317. padding: 0;
  318. }
  319. .todo-list li {
  320. background-color: rgba(0, 0, 0, 0.05);
  321. padding: 0.5rem 1rem;
  322. border-radius: 10px;
  323. margin-bottom: 0.5rem;
  324. cursor: pointer;
  325. }
  326. .todo-list li:hover {
  327. background-color: rgba(0, 0, 0, 0.1);
  328. }
  329. .theme-button {
  330. padding: 0.75rem 1.5rem;
  331. background-color: #007aff;
  332. color: #fff;
  333. border: none;
  334. border-radius: 10px;
  335. cursor: pointer;
  336. }
  337. `;
  338.  
  339. // Inject the custom CSS into the page
  340. const styleSheet = document.createElement("style");
  341. styleSheet.type = "text/css";
  342. styleSheet.innerText = customCss;
  343. document.head.appendChild(styleSheet);
  344.  
  345. })();