Initial commit

This commit is contained in:
2025-07-16 15:12:58 +00:00
commit 6a728ecd8e
7 changed files with 626 additions and 0 deletions

276
extension.js Normal file
View File

@ -0,0 +1,276 @@
'use strict';
import St from 'gi://St';
import Clutter from 'gi://Clutter';
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import Soup from 'gi://Soup';
import GObject from 'gi://GObject';
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
class QuotesExtension extends Extension {
constructor(metadata) {
super(metadata);
this._quotes = [];
this._currentQuoteIndex = 0;
this._rotationTimer = null;
this._syncTimer = null;
this._indicator = null;
this._session = null;
this._dataDir = null;
this._quotesFile = null;
this._settings = null;
}
enable() {
this._initializeSettings();
this._initializeDataDir();
this._loadQuotes();
this._createIndicator();
this._setupTimers();
this._syncQuotes();
}
disable() {
if (this._rotationTimer) {
GLib.source_remove(this._rotationTimer);
this._rotationTimer = null;
}
if (this._syncTimer) {
GLib.source_remove(this._syncTimer);
this._syncTimer = null;
}
if (this._indicator) {
this._indicator.destroy();
this._indicator = null;
}
if (this._session) {
this._session = null;
}
}
_initializeDataDir() {
this._dataDir = GLib.build_filenamev([GLib.get_user_data_dir(), 'quotes-extension']);
GLib.mkdir_with_parents(this._dataDir, 0o755);
const fileName = this._settings ? this._settings.get_string('quotes-file') || 'quotes.json' : 'quotes.json';
this._quotesFile = GLib.build_filenamev([this._dataDir, fileName]);
}
_initializeSettings() {
this._settings = this.getSettings();
this._rotationInterval = this._settings.get_int('rotation-interval') || 30;
this._syncInterval = this._settings.get_int('sync-interval') || 3600;
this._apiUrl = this._settings.get_string('api-url') || 'https://api.quotable.io/random';
this._fontSize = this._settings.get_int('font-size') || 14;
this._textColor = this._settings.get_string('text-color') || '#ffffff';
this._maxWidth = this._settings.get_int('max-width') || 60;
this._quotesFileName = this._settings.get_string('quotes-file') || 'quotes.json';
this._useCustomQuotes = this._settings.get_boolean('use-custom-quotes');
this._customQuotes = this._settings.get_string('custom-quotes') || '[]';
// Listen for settings changes
this._settings.connect('changed', (settings, key) => {
if (key === 'font-size' || key === 'text-color') {
this._updateLabelStyle();
} else if (key === 'max-width') {
this._maxWidth = this._settings.get_int('max-width') || 60;
this._label.set_text(this._getCurrentQuote());
} else if (key === 'quotes-file') {
this._quotesFileName = this._settings.get_string('quotes-file') || 'quotes.json';
this._initializeDataDir();
this._loadQuotes();
} else if (key === 'use-custom-quotes' || key === 'custom-quotes') {
this._useCustomQuotes = this._settings.get_boolean('use-custom-quotes');
this._customQuotes = this._settings.get_string('custom-quotes') || '[]';
this._loadQuotes();
if (this._label) {
this._label.set_text(this._getCurrentQuote());
}
}
});
}
_loadQuotes() {
this._quotes = [];
// Use custom quotes if enabled
if (this._useCustomQuotes) {
try {
this._quotes = JSON.parse(this._customQuotes);
if (!Array.isArray(this._quotes)) {
this._quotes = [];
}
} catch (e) {
console.error('Failed to parse custom quotes:', e);
this._quotes = [];
}
} else {
// Load from file
try {
if (GLib.file_test(this._quotesFile, GLib.FileTest.EXISTS)) {
const [success, contents] = GLib.file_get_contents(this._quotesFile);
if (success) {
const decoder = new TextDecoder();
this._quotes = JSON.parse(decoder.decode(contents));
}
}
} catch (e) {
console.error('Failed to load quotes:', e);
}
}
if (this._quotes.length === 0) {
this._quotes = [
{ text: "The only way to do great work is to love what you do.", author: "Steve Jobs" },
{ text: "Life is what happens to you while you're busy making other plans.", author: "John Lennon" },
{ text: "The future belongs to those who believe in the beauty of their dreams.", author: "Eleanor Roosevelt" },
{ text: "It is during our darkest moments that we must focus to see the light.", author: "Aristotle" },
{ text: "Success is not final, failure is not fatal: it is the courage to continue that counts.", author: "Winston Churchill" },
{ text: "The way to get started is to quit talking and begin doing.", author: "Walt Disney" },
{ text: "Don't be afraid to give up the good to go for the great.", author: "John D. Rockefeller" },
{ text: "Innovation distinguishes between a leader and a follower.", author: "Steve Jobs" },
{ text: "Your time is limited, don't waste it living someone else's life.", author: "Steve Jobs" },
{ text: "If you look at what you have in life, you'll always have more.", author: "Oprah Winfrey" },
{ text: "The only impossible journey is the one you never begin.", author: "Tony Robbins" },
{ text: "In the midst of winter, I found there was, within me, an invincible summer.", author: "Albert Camus" },
{ text: "Be yourself; everyone else is already taken.", author: "Oscar Wilde" },
{ text: "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe.", author: "Albert Einstein" },
{ text: "You miss 100% of the shots you don't take.", author: "Wayne Gretzky" },
{ text: "Whether you think you can or you think you can't, you're right.", author: "Henry Ford" },
{ text: "I have learned throughout my life as a composer chiefly through my mistakes and pursuits of false assumptions.", author: "Igor Stravinsky" },
{ text: "The greatest glory in living lies not in never falling, but in rising every time we fall.", author: "Nelson Mandela" },
{ text: "The only person you are destined to become is the person you decide to be.", author: "Ralph Waldo Emerson" },
{ text: "What lies behind us and what lies before us are tiny matters compared to what lies within us.", author: "Ralph Waldo Emerson" },
{ text: "Believe you can and you're halfway there.", author: "Theodore Roosevelt" },
{ text: "The best time to plant a tree was 20 years ago. The second best time is now.", author: "Chinese Proverb" },
{ text: "Don't judge each day by the harvest you reap but by the seeds that you plant.", author: "Robert Louis Stevenson" },
{ text: "The only way to make sense out of change is to plunge into it, move with it, and join the dance.", author: "Alan Watts" },
{ text: "Yesterday is history, tomorrow is a mystery, today is a gift of God, which is why we call it the present.", author: "Bill Keane" }
];
this._saveQuotes();
}
}
_saveQuotes() {
try {
const contents = JSON.stringify(this._quotes, null, 2);
GLib.file_set_contents(this._quotesFile, contents);
} catch (e) {
console.error('Failed to save quotes:', e);
}
}
_createIndicator() {
this._indicator = new PanelMenu.Button(0.0, 'Quotes', false);
this._label = new St.Label({
text: this._getCurrentQuote(),
y_align: Clutter.ActorAlign.CENTER,
style_class: 'quote-label'
});
this._updateLabelStyle();
this._indicator.add_child(this._label);
Main.panel.addToStatusArea('quotes', this._indicator, 1, 'left');
this._indicator.connect('button-press-event', () => {
this._nextQuote();
});
}
_getCurrentQuote() {
if (this._quotes.length === 0) return 'No quotes available';
const quote = this._quotes[this._currentQuoteIndex];
let text = `"${quote.text}" - ${quote.author}`;
if (text.length > this._maxWidth) {
text = text.substring(0, this._maxWidth - 3) + '...';
}
return text;
}
_updateLabelStyle() {
if (!this._label) return;
this._fontSize = this._settings.get_int('font-size') || 14;
this._textColor = this._settings.get_string('text-color') || '#ffffff';
const style = `font-size: ${this._fontSize}px; color: ${this._textColor};`;
this._label.set_style(style);
}
_nextQuote() {
if (this._quotes.length === 0) return;
this._currentQuoteIndex = (this._currentQuoteIndex + 1) % this._quotes.length;
this._label.set_text(this._getCurrentQuote());
}
_setupTimers() {
this._rotationTimer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
this._rotationInterval, () => {
this._nextQuote();
return GLib.SOURCE_CONTINUE;
});
this._syncTimer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
this._syncInterval, () => {
this._syncQuotes();
return GLib.SOURCE_CONTINUE;
});
}
_syncQuotes() {
try {
this._session = new Soup.Session();
const message = Soup.Message.new('GET', this._apiUrl);
this._session.send_and_read_async(message, GLib.PRIORITY_DEFAULT, null, (session, result) => {
try {
const bytes = session.send_and_read_finish(result);
const decoder = new TextDecoder();
const response = decoder.decode(bytes.get_data());
const data = JSON.parse(response);
if (data.content && data.author) {
const newQuote = {
text: data.content,
author: data.author
};
const exists = this._quotes.some(q =>
q.text === newQuote.text && q.author === newQuote.author);
if (!exists) {
this._quotes.push(newQuote);
this._saveQuotes();
}
}
} catch (e) {
console.error('Failed to sync quotes:', e);
}
});
} catch (e) {
console.error('Failed to create sync session:', e);
}
}
}
export default class QuotesGnomeExtension extends Extension {
enable() {
this._extension = new QuotesExtension(this.metadata);
this._extension.enable();
}
disable() {
if (this._extension) {
this._extension.disable();
this._extension = null;
}
}
}