תחרות מוחות בין הAI השונים
-
מקום שלישי פריבף
ניצוץ (NITZOTZ) — מנהל לוח־גזירים חכם בעברית
מה זה: כלי שיושב ברקע ומנטר כל העתקה במחשב. הוא מזהה אוטומטית מה העתקת (קישור, טלפון, מייל, מספר מעקב, סכום כסף, IP, ברקוד וכו'), שומר היסטוריה מוצפנת מקומית, ומציע לכל פריט פעולות חכמות בלחיצה אחת — בלי לשלוח שום דבר לאינטרנט.טכנולוגיה: C# / WinForms על .NET 8 — EXE יחיד עצמאי, ללא Python/Node, עם RTL מלא, אייקון מקורי וקיצור מקשים גלובלי.
הקוד המלא [לא נבדק]
$ErrorActionPreference = 'Stop' $Host.UI.RawUI.WindowTitle = 'בונה את ניצוץ...' Write-Host "`n === ניצוץ (NITZOTZ) - בניית מנהל לוח הגזירים ===" -ForegroundColor Cyan function Say($t,$c='Gray'){ Write-Host " $t" -ForegroundColor $c } $root = Join-Path $env:USERPROFILE 'NitzotzBuild' $out = Join-Path ([Environment]::GetFolderPath('Desktop')) 'ניצוץ' $tmp = Join-Path $env:TEMP 'nitzotz_tmp' Remove-Item $root,$tmp -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $root,$tmp,$out | Out-Null Set-Location $root # ---------- 1. הבטחת .NET 8 SDK ---------- $dotnet = (Get-Command dotnet -ErrorAction SilentlyContinue) function HaveSdk { try { return ((& dotnet --list-sdks) -match '^8\.').Count -gt 0 } catch { return $false } } if(-not $dotnet -or -not (HaveSdk)){ Say 'מוריד .NET 8 SDK (פעם אחת בלבד, ~200MB)...' Yellow $sdk = Join-Path $tmp 'dotnet-sdk-win-x64.exe' try { Invoke-WebRequest 'https://aka.ms/dotnet/8.0/dotnet-sdk-win-x64.exe' -OutFile $sdk -UseBasicParsing -TimeoutSec 600 } catch { Say 'ההורדה הרגילה נכשלה, מנסה מקור חלופי...' Yellow Invoke-WebRequest 'https://builds.dotnet.microsoft.com/dotnet/Sdk/8.0.404/dotnet-sdk-8.0.404-win-x64.exe' -OutFile $sdk -UseBasicParsing -TimeoutSec 600 } Say 'מתקין את .NET SDK...' Yellow Start-Process $sdk -ArgumentList '/install','/quiet','/norestart' -Wait $env:PATH = "$env:LOCALAPPDATA\Microsoft\dotnet;$env:PATH" if(-not (Get-Command dotnet -ErrorAction SilentlyContinue)){ $env:PATH = "${env:ProgramFiles}\dotnet;$env:PATH" } } $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' $env:DOTNET_NOLOGO = '1' if(-not (HaveSdk)){ throw 'התקנת .NET SDK נכשלה. אנא התקן ידנית מ-https://dotnet.microsoft.com/download/dotnet/8.0 וחזור.' } Say ('נמצא .NET SDK: ' + ((& dotnet --version))) Green # ---------- 2. קובץ פרויקט ---------- $csproj = @' <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net8.0-windows</TargetFramework> <UseWindowsForms>true</UseWindowsForms> <Nullable>disable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <ApplicationIcon>app.ico</ApplicationIcon> <AssemblyName>Nitzotz</AssemblyName> <Product>ניצוץ</Product> <Company>Nitzotz</Company> <Version>1.0.0</Version> <ApplicationManifest>app.manifest</ApplicationManifest> <InvariantGlobalization>false</InvariantGlobalization> <PublishSingleFile>true</PublishSingleFile> <SelfContained>true</SelfContained> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <PublishTrimmed>false</PublishTrimmed> <DebugType>none</DebugType> <SatelliteResourceLanguages>he</SatelliteResourceLanguages> </PropertyGroup> </Project> '@ Set-Content "$root\Nitzotz.csproj" $csproj -Encoding UTF8 $manifest = @' <?xml version="1.0" encoding="utf-8"?> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyIdentity version="1.0.0.0" name="Nitzotz.app"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> </application> </compatibility> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2</dpiAwareness> </windowsSettings> </application> </assembly> '@ Set-Content "$root\app.manifest" $manifest -Encoding UTF8 # ---------- 3. יצירת אייקון מקורי (ICO) ללא תלות חיצונית ---------- $iconCode = @' using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; class IconGen { static void Main(string[] args){ string outPath = args[0]; int[] sizes = { 16, 24, 32, 48, 64, 128, 256 }; var pngs = new List<byte[]>(); foreach(int s in sizes) { using(var bmp = new Bitmap(s,s)) { using(var g = Graphics.FromImage(bmp)) { g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.Clear(Color.Transparent); float r = s * 0.20f; using(var path = Rounded(new RectangleF(0,0,s,s), r)) { using(var br = new LinearGradientBrush(new RectangleF(0,0,s,s), Color.FromArgb(255,28,32,48), Color.FromArgb(255,12,14,22), 45f)) g.FillPath(br, path); } using(var pen = new Pen(Color.FromArgb(90,0,220,200), Math.Max(1f,s*0.035f))) using(var path = Rounded(new RectangleF(s*0.03f,s*0.03f,s*0.94f,s*0.94f), r*0.9f)) g.DrawPath(pen, path); float cx = s*0.5f, cy = s*0.46f; var spark = new PointF[] { new PointF(cx, cy - s*0.30f), new PointF(cx + s*0.11f, cy - s*0.05f), new PointF(cx + s*0.34f, cy - s*0.01f), new PointF(cx + s*0.14f, cy + s*0.09f), new PointF(cx + s*0.21f, cy + s*0.32f), new PointF(cx, cy + s*0.17f), new PointF(cx - s*0.21f, cy + s*0.32f), new PointF(cx - s*0.14f, cy + s*0.09f), new PointF(cx - s*0.34f, cy - s*0.01f), new PointF(cx - s*0.11f, cy - s*0.05f) }; using(var glow = new SolidBrush(Color.FromArgb(60,0,230,200))) { var big = new PointF[spark.Length]; for(int i=0;i<spark.Length;i++){ big[i]=new PointF(cx+(spark[i].X-cx)*1.25f, cy+(spark[i].Y-cy)*1.25f); } g.FillPolygon(glow, big); } using(var br = new LinearGradientBrush(new RectangleF(cx-s*0.3f,cy-s*0.3f,s*0.6f,s*0.6f), Color.FromArgb(255,255,214,90), Color.FromArgb(255,0,224,192), 60f)) g.FillPolygon(br, spark); using(var clipPen = new Pen(Color.FromArgb(230,235,240,250), Math.Max(1f,s*0.07f))) clipPen.StartCap = clipPen.EndCap = LineCap.Round; using(var clipPen2 = new Pen(Color.FromArgb(230,235,240,250), Math.Max(1f,s*0.07f))) clipPen2.StartCap = clipPen2.EndCap = LineCap.Round; if(s >= 24){ using(var bp = new Pen(Color.FromArgb(255,150,200,255), Math.Max(1f,s*0.055f))) using(var path = Rounded(new RectangleF(cx+s*0.12f, cy+s*0.10f, s*0.26f, s*0.36f), s*0.07f)) g.DrawPath(bp, path); } } using(var ms = new MemoryStream()){ bmp.Save(ms, ImageFormat.Png); pngs.Add(ms.ToArray()); } } } using(var fs = new FileStream(outPath, FileMode.Create)) using(var bw = new BinaryWriter(fs)) { bw.Write((ushort)0); bw.Write((ushort)1); bw.Write((ushort)pngs.Count); int offset = 6 + 16*pngs.Count; for(int i=0;i<pngs.Count;i++){ int s = sizes[i]; bw.Write((byte)(s>=256?0:s)); bw.Write((byte)(s>=256?0:s)); bw.Write((byte)0); bw.Write((byte)0); bw.Write((ushort)1); bw.Write((ushort)32); bw.Write((uint)pngs[i].Length); bw.Write((uint)offset); offset += pngs[i].Length; } foreach(var p in pngs) bw.Write(p); } Console.WriteLine("ICON_OK"); } static GraphicsPath Rounded(RectangleF r, float rad){ var p = new GraphicsPath(); float d = rad*2; p.AddArc(r.X, r.Y, d, d, 180, 90); p.AddArc(r.Right-d, r.Y, d, d, 270, 90); p.AddArc(r.Right-d, r.Bottom-d, d, d, 0, 90); p.AddArc(r.X, r.Bottom-d, d, d, 90, 90); p.CloseFigure(); return p; } } '@ Set-Content "$root\IconGen.cs" $iconCode -Encoding UTF8 # ---------- 4. קוד האפליקציה ---------- $app = @' using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using Microsoft.Win32; namespace Nitzotz { static class Theme { public static readonly Color Bg = Color.FromArgb(16,18,28); public static readonly Color Bg2 = Color.FromArgb(24,27,40); public static readonly Color Card = Color.FromArgb(30,34,50); public static readonly Color CardHi = Color.FromArgb(40,46,66); public static readonly Color Accent = Color.FromArgb(0,224,192); public static readonly Color Accent2 = Color.FromArgb(255,205,80); public static readonly Color Text = Color.FromArgb(236,240,248); public static readonly Color Sub = Color.FromArgb(150,160,185); public static readonly Color Danger = Color.FromArgb(245,90,110); public static Font F(int sz, FontStyle st = FontStyle.Regular) { try { return new Font("Segoe UI", sz, st); } catch { return new Font(FontFamily.GenericSansSerif, sz, st); } } } public enum ClipKind { Link, Email, Phone, Money, Ip, Code, Track, Location, Text, Path, Number } public class ClipItem { public string Id = Guid.NewGuid().ToString("N"); public string Text = ""; public ClipKind Kind = ClipKind.Text; public string Label = "טקסט"; public DateTime When = DateTime.Now; public bool Pinned = false; public string Hash = ""; } static class Classifier { static readonly Regex ReUrl = new(@"^(https?://|www\.)[^\s]+$", RegexOptions.IgnoreCase); static readonly Regex ReEmail = new(@"^[\w\.\-+]+@[\w\-]+\.[\w\.\-]+$"); static readonly Regex RePhone = new(@"^(\+?\d[\d\-\s\(\)]{6,}\d)$"); static readonly Regex ReMoney = new(@"^[₪$€£]\s?\d[\d,\.]*(\s?[₪$€£]|k|K|₪)?$"); static readonly Regex ReIp = new(@"^((25[0-5]|2[0-4]\d|1?\d?\d)(\.|$)){4}$"); static readonly Regex ReTrack = new(@"\b\d{9,22}\b"); static readonly Regex RePath = new(@"^[A-Za-z]:\\[^\r\n]+$|^\\\\[^\r\n]+$"); static readonly Regex ReMap = new(@"^(-?\d{1,3}\.\d{3,}),\s*(-?\d{1,3}\.\d{3,})$"); static readonly Regex ReCoord = new(@"^\d{1,2}[°\s]\d{1,2}['\u2032\s][\d\.]+[""'\u2033\s]?[NSנצ]?[,\s]+\d{1,3}[°\s]\d{1,2}['\u2032\s][\d\.]+[""'\u2033\s]?[EWמז]?", RegexOptions.IgnoreCase); public static (ClipKind, string) Classify(string t) { t = t.Trim(); if (string.IsNullOrWhiteSpace(t)) return (ClipKind.Text, "ריק"); if (ReUrl.IsMatch(t)) return (ClipKind.Link, "קישור"); if (ReEmail.IsMatch(t)) return (ClipKind.Email, "דואר אלקטרוני"); if (RePath.IsMatch(t)) return (ClipKind.Path, "נתיב קובץ"); if (ReMap.IsMatch(t)) return (ClipKind.Location, "מיקום"); if (ReIp.IsMatch(t)) return (ClipKind.Ip, "כתובת IP"); if (RePhone.IsMatch(t) && t.Count(char.IsDigit) >= 7) return (ClipKind.Phone, "מספר טלפון"); if (ReMoney.IsMatch(t)) return (ClipKind.Money, "סכום כסף"); if (LooksLikeCode(t)) return (ClipKind.Code, "קוד"); if (ReTrack.IsMatch(t) && t.Count(char.IsDigit) >= 9 && t.Count(char.IsWhiteSpace) <= 1) return (ClipKind.Track, "מספר מעקב"); if (t.All(c => char.IsDigit(c) || char.IsPunctuation(c) || char.IsWhiteSpace(c)) && t.Count(char.IsDigit) >= 3) return (ClipKind.Number, "מספר"); return (ClipKind.Text, "טקסט"); } static bool LooksLikeCode(string t) { string[] marks = { "function ", "def ", "class ", "public ", "private ", "#include", "import ", "const ", "=>", "</", "SELECT ", "<?php", "console.log", "#!/", "async ", "return " }; return marks.Any(m => t.Contains(m, StringComparison.OrdinalIgnoreCase)); } } static class History { static string Dir { get { var d = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Nitzotz"); Directory.CreateDirectory(d); return d; } } static string FilePath => Path.Combine(Dir, "history.dat"); static string Hash(string s){ using var sha = SHA256.Create(); return Convert.ToHexString(sha.ComHash(Encoding.UTF8.GetBytes(s.Trim()))); } public static List<ClipItem> Load() { try { if(!File.Exists(FilePath)) return new List<ClipItem>(); byte[] enc = File.ReadAllBytes(FilePath); var plain = ProtectedData.Unprotect(enc, null, DataProtectionScope.CurrentUser); var lines = Encoding.UTF8.GetString(plain).Split('\u0001'); var list = new List<ClipItem>(); foreach(var ln in lines){ if(string.IsNullOrWhiteSpace(ln)) continue; var p = ln.Split('\u0002'); if(p.Length < 5) continue; var it = new ClipItem { Text = FromB64(p[0]), Kind = (ClipKind)int.Parse(p[1]), When = DateTime.FromBinary(long.Parse(p[2])), Pinned = p[3]=="1", Id = p.Length>4?p[4]:Guid.NewGuid().ToString("N") }; it.Label = KindLabel(it.Kind); it.Hash = Hash(it.Text); list.Add(it); } return list; } catch { return new List<ClipItem>(); } } public static void Save(List<ClipItem> list) { try { var sb = new StringBuilder(); foreach(var it in list){ sb.Append(ToB64(it.Text)).Append('\u0002') .Append((int)it.Kind).Append('\u0002') .Append(it.When.ToBinary()).Append('\u0002') .Append(it.Pinned?"1":"0").Append('\u0002') .Append(it.Id).Append('\u0001'); } var enc = ProtectedData.Protect(Encoding.UTF8.GetBytes(sb.ToString()), null, DataProtectionScope.CurrentUser); File.WriteAllBytes(FilePath, enc); } catch { } } public static void Clear(){ try{ File.Delete(FilePath);}catch{} } static string ToB64(string s)=> Convert.ToBase64String(Encoding.UTF8.GetBytes(s)); static string FromB64(string s)=> Encoding.UTF8.GetString(Convert.FromBase64String(s)); public static string KindLabel(ClipKind k)=> k switch { ClipKind.Link=>"קישור", ClipKind.Email=>"דואר אלקטרוני", ClipKind.Phone=>"מספר טלפון", ClipKind.Money=>"סכום כסף", ClipKind.Ip=>"כתובת IP", ClipKind.Code=>"קוד", ClipKind.Track=>"מספר מעקב", ClipKind.Location=>"מיקום", ClipKind.Path=>"נתיב קובץ", ClipKind.Number=>"מספר", _=>"טקסט" }; } class Card : Panel { public ClipItem Item; public bool Hover; public Action<ClipItem,string> OnAction; public Card(ClipItem it){ Item = it; DoubleBuffered = true; Margin = new Padding(0,0,0,10); Height = 92; Cursor = Cursors.Hand; SetStyle(ControlStyles.AllPaintingInWmPaint|ControlStyles.UserPaint|ControlStyles.OptimizedDoubleBuffer, true); MouseEnter += (s,e)=>{ Hover=true; Invalidate(); }; MouseLeave += (s,e)=>{ Hover=false; Invalidate(); }; } string Preview(){ var t = Item.Text.Replace("\r"," ").Replace("\n"," ").Trim(); if(t.Length>110) t = t.Substring(0,110)+"…"; return t; } protected override void OnPaint(PaintEventArgs e){ var g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; var rect = new Rectangle(1,1,Width-3,Height-3); Color fill = Hover ? Theme.CardHi : Theme.Card; using(var path = Rounded(rect,12)) using(var br = new SolidBrush(fill)) g.FillPath(br,path); var kindColor = KindColor(Item.Kind); using(var bar = new SolidBrush(kindColor)) using(var p = Rounded(new RectangleF(rect.Width-6, 8, 4, Height-18), 2)) g.FillPath(bar, p); using(var badge = new SolidBrush(Color.FromArgb(38, kindColor))) using(var bp = Rounded(new RectangleF(14,14, badgeWidth, 24), 8)) g.FillPath(badge, bp); using(var f = Theme.F(9.5f, FontStyle.Bold)) using(var tb = new SolidBrush(kindColor)) g.DrawString(Item.Label, f, tb, 24, 18); using(var f = Theme.F(11f)) using(var tb = new SolidBrush(Theme.Text)) g.DrawString(Preview(), f, tb, new RectangleF(16, 42, Width-90, 40)); if(Item.Pinned){ using(var f = Theme.F(12f)) using(var tb = new SolidBrush(Theme.Accent2)) g.DrawString("📌", f, tb, Width-52, 14); } using(var f = Theme.F(8.5f)) using(var tb = new SolidBrush(Theme.Sub)) g.DrawString(Item.When.ToString("HH:mm · dd/MM"), f, tb, Width-108, Height-26); if(Hover){ DrawHoverButtons(g, rect); } } float badgeWidth => 14 + Item.Label.Length*8f; void DrawHoverButtons(Graphics g, Rectangle rect){ var acts = Actions(); int bx = 16, by = Height-40; foreach(var a in acts){ using(var p = Rounded(new RectangleF(bx,by,a.W,26),8)) using(var br = new SolidBrush(Color.FromArgb(230,Theme.Bg2))) g.FillPath(br,p); using(var f = Theme.F(8.5f,FontStyle.Bold)) using(var tb = new SolidBrush(Theme.Accent)) g.DrawString(a.T, f, tb, bx+ (a.W- g.MeasureString(a.T,f).Width)/2, by+7); var lx = bx; var lt = a.T; var la = a.A; HitAreas.Add(new Rectangle(bx,by,a.W,26), la); bx += a.W + 8; } } public List<(string T,int W,string A)> Actions(){ var l = new List<(string,int,string)>(); switch(Item.Kind){ case ClipKind.Link: l.Add(("פתח בדפדפן",96,"open")); l.Add(("העתק",52,"copy")); break; case ClipKind.Email: l.Add(("שלח מייל",80,"mail")); l.Add(("העתק",52,"copy")); break; case ClipKind.Phone: l.Add(("חייג",52,"call")); l.Add(("WhatsApp",76,"wa")); l.Add(("העתק",52,"copy")); break; case ClipKind.Location: l.Add(("פתח במפות",96,"maps")); l.Add(("העתק",52,"copy")); break; case ClipKind.Path: l.Add(("פתח בתיקייה",92,"explore")); l.Add(("העתק",52,"copy")); break; case ClipKind.Money: l.Add(("חשב",56,"calc")); l.Add(("העתק",52,"copy")); break; default: l.Add(("העתק",52,"copy")); break; } l.Add((Item.Pinned?"בטל הצמדה":"הצמד",80,"pin")); l.Add(("מחק",52,"del")); return l; } public Dictionary<Rectangle,string> HitAreas = new Dictionary<Rectangle,string>(); public string Hit(Point p){ foreach(var kv in HitAreas) if(kv.Key.Contains(p)) return kv.Value; return null; } public static Color KindColor(ClipKind k)=> k switch { ClipKind.Link=>Color.FromArgb(80,170,255), ClipKind.Email=>Color.FromArgb(255,150,210), ClipKind.Phone=>Color.FromArgb(0,224,192), ClipKind.Money=>Color.FromArgb(255,205,80), ClipKind.Ip=>Color.FromArgb(170,150,255), ClipKind.Code=>Color.FromArgb(255,140,120), ClipKind.Track=>Color.FromArgb(140,220,140), ClipKind.Location=>Color.FromArgb(255,180,120), ClipKind.Path=>Color.FromArgb(200,180,255), ClipKind.Number=>Color.FromArgb(150,180,200), _=>Color.FromArgb(150,160,185) }; public static GraphicsPath Rounded(RectangleF r, float rad){ var p = new GraphicsPath(); float d = rad*2; p.AddArc(r.X,r.Y,d,d,180,90); p.AddArc(r.Right-d,r.Y,d,d,270,90); p.AddArc(r.Right-d,r.Bottom-d,d,d,0,90); p.AddArc(r.X,r.Bottom-d,d,d,90,90); p.CloseFigure(); return p; } } class MainForm : Form { List<ClipItem> items = new List<ClipItem>(); FlowLayoutPanel listPanel; TextBox search; string filter = "הכל"; Timer clipTimer; string lastHash = ""; NotifyIcon tray; IntPtr hotkeyId = (IntPtr)0xBEEF; [DllImport("user32.dll")] static extern bool RegisterHotKey(IntPtr h, int id, int mod, int key); [DllImport("user32.dll")] static extern bool UnregisterHotKey(IntPtr h, int id); public MainForm(){ Text = "ניצוץ — מנהל לוח הגזירים החכם"; Size = new Size(560, 720); MinimumSize = new Size(460, 520); StartPosition = FormStartPosition.CenterScreen; BackColor = Theme.Bg; RightToLeft = RightToLeft.Yes; RightToLeftLayout = true; Font = Theme.F(10f); Icon = LoadIco(); BuildUI(); items = History.Load(); RefreshList(); clipTimer = new Timer { Interval = 700 }; clipTimer.Tick += (s,e)=> Poll(); clipTimer.Start(); } static Icon LoadIco(){ try { var p = Path.Combine(AppContext.BaseDirectory,"app.ico"); if(File.Exists(p)) return new Icon(p); var exe = System.Reflection.Assembly.GetExecutingAssembly().Location; return Icon.ExtractAssociatedIcon(exe); } catch { return SystemIcons.Application; } } void BuildUI(){ var header = new Panel { Dock = DockStyle.Top, Height = 138, BackColor = Theme.Bg }; header.Paint += (s,e)=>{ var g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; using(var br = new LinearGradientBrush(header.ClientRectangle, Theme.Bg2, Theme.Bg, 90f)) g.FillRectangle(br, header.ClientRectangle); using(var f = Theme.F(20f, FontStyle.Bold)) using(var tb = new SolidBrush(Theme.Text)) g.DrawString("ניצוץ", f, tb, new PointF(header.Width-70, 14)); using(var f = Theme.F(18f)) using(var tb = new SolidBrush(Theme.Accent)) g.DrawString("✦", f, tb, new PointF(header.Width-108, 16)); using(var f = Theme.F(9.5f)) using(var tb = new SolidBrush(Theme.Sub)) g.DrawString("כל מה שהעתקת — שמור, מסווג ומוכן לפעולה", f, tb, new PointF(header.Width-330, 52)); int x = 20; using(var f = Theme.F(9f, FontStyle.Bold)) using(var tb = new SolidBrush(Theme.Sub)) g.DrawString($"{items.Count} פריטים", f, tb, new PointF(x, 20)); }; Controls.Add(header); var topPanel = new Panel { Dock = DockStyle.Top, Height = 46, BackColor = Theme.Bg, Padding = new Padding(16,0,16,0) }; search = new TextBox { Dock = DockStyle.Fill, BorderStyle = BorderStyle.FixedSingle, BackColor = Theme.Card, ForeColor = Theme.Text, Font = Theme.F(11f), RightToLeft = RightToLeft.Yes }; var searchHost = new Panel { Dock = DockStyle.Top, Height = 40, Padding = new Padding(0,0,0,8) }; searchHost.Controls.Add(search); var hint = new Label { Dock = DockStyle.Fill, Text = "🔍 חיפוש בהיסטוריה...", ForeColor = Theme.Sub, BackColor = Theme.Card, TextAlign = ContentAlignment.MiddleRight, Padding = new Padding(8,0,8,0), Font = Theme.F(10.5f), Cursor = Cursors.IBeam }; hint.Click += (s,e)=> search.Focus(); searchHost.Controls.Add(hint); search.TextChanged += (s,e)=>{ hint.Visible = search.Text.Length==0; RefreshList(); }; search.Enter += (s,e)=>{ }; header.Controls.Add(searchHost); searchHost.SetBounds(16, 84, header.Width-32, 40); header.Resize += (s,e)=> searchHost.SetBounds(16, 84, header.Width-32, 40); var filters = new FlowLayoutPanel { Dock = DockStyle.Top, Height = 44, BackColor = Theme.Bg, FlowDirection = FlowDirection.RightToLeft, Padding = new Padding(12,4,12,4) }; string[] ff = { "הכל","קישור","טלפון","דואר אלקטרוני","מספר טracking".Replace("tracking","מעקב"),"סכום כסף","קוד","מיקום","מוצמד" }; foreach(var f in ff){ var b = new Button { Text = f, AutoSize = true, FlatStyle = FlatStyle.Flat, Height = 30, BackColor = f==filter?Theme.Accent:Theme.Card, ForeColor = f==filter?Theme.Bg:Theme.Text, Font = Theme.F(9.5f), Margin = new Padding(4,0,0,0), Cursor = Cursors.Hand, Padding = new Padding(8,0,8,0) }; b.FlatAppearance.BorderSize = 0; b.Click += (s,e)=>{ filter = b.Text; foreach(Control c in filters.Controls){ var bb=c as Button; bb.BackColor = bb.Text==filter?Theme.Accent:Theme.Card; bb.ForeColor = bb.Text==filter?Theme.Bg:Theme.Text; } RefreshList(); }; filters.Controls.Add(b); } Controls.Add(filters); var footer = new Panel { Dock = DockStyle.Bottom, Height = 44, BackColor = Theme.Bg2 }; var clear = new Button { Text = "🗑 נקה הכול", Dock = DockStyle.Right, Width = 120, FlatStyle = FlatStyle.Flat, BackColor = Theme.Bg2, ForeColor = Theme.Danger, Font = Theme.F(10f, FontStyle.Bold) }; clear.FlatAppearance.BorderSize = 0; clear.Click += (s,e)=>{ if(MessageBox.Show(this,"למחוק את כל ההיסטוריה? הפעולה בלתי הפיכה.","ניצוץ",MessageBoxButtons.YesNo,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button2, MessageBoxOptions.RtlReading)==DialogResult.Yes){ History.Clear(); items.Clear(); RefreshList(); } }; var paste = new Button { Text = "📋 העתק שוב", Dock = DockStyle.Left, Width = 140, FlatStyle = FlatStyle.Flat, BackColor = Theme.Bg2, ForeColor = Theme.Accent, Font = Theme.F(10f, FontStyle.Bold) }; paste.FlatAppearance.BorderSize = 0; paste.Click += (s,e)=>{ try{ if(Clipboard.ContainsText()) AddOrPromote(Clipboard.GetText(), true); }catch{} }; footer.Controls.Add(clear); footer.Controls.Add(paste); listPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.TopDown, WrapContents = false, AutoScroll = true, BackColor = Theme.Bg, Padding = new Padding(16,8,16,8) }; listPanel.Resize += (s,e)=> ResizeCards(); Controls.Add(listPanel); Controls.Add(footer); } void ResizeCards(){ foreach(Control c in listPanel.Controls) if(c is Card) c.Width = listPanel.ClientSize.Width - 40; } void Poll(){ try { if(!Clipboard.ContainsText()) return; string t = Clipboard.GetText(); if(string.IsNullOrWhiteSpace(t)) return; var (kind,_) = Classifier.Classify(t); if(kind == ClipKind.Code && t.Length > 20000) return; string h = ""; using(var sha = SHA256.Create()) h = Convert.ToHexString(sha.ComHash(Encoding.UTF8.GetBytes(t.Trim()))); if(h == lastHash) return; lastHash = h; if(items.Any(i=> i.Hash==h)) return; AddOrPromote(t, false); } catch { } } void AddOrPromote(string text, bool manual){ text = text.Trim(); if(text.Length > 50000) text = text.Substring(0,50000); var (kind,label) = Classifier.Classify(text); var item = new ClipItem { Text = text, Kind = kind, Label = label, When = DateTime.Now }; using(var sha = SHA256.Create()) item.Hash = Convert.ToHexString(sha.ComHash(Encoding.UTF8.GetBytes(text))); items.Insert(0, item); if(items.Count > 500) items = items.Take(500).ToList(); History.Save(items); RefreshList(); } void RefreshList(){ listPanel.SuspendLayout(); foreach(Control c in listPanel.Controls) c.Dispose(); listPanel.Controls.Clear(); IEnumerable<ClipItem> q = items; if(filter == "מוצמד") q = q.Where(i=>i.Pinned); else if(filter != "הכל") q = q.Where(i=> i.Label==filter); if(!string.IsNullOrWhiteSpace(search.Text)) q = q.Where(i=> i.Text.Contains(search.Text, StringComparison.OrdinalIgnoreCase)); foreach(var it in q.OrderByDescending(i=>i.Pinned)){ var card = new Card(it); card.Width = listPanel.ClientSize.Width - 40; card.OnAction = DoAction; card.Click += (s,e)=> { var p = card.PointToClient(Cursor.Position); var a = card.Hit(p); if(a!=null) DoAction(it, a); }; card.MouseMove += (s,e)=> { var a = card.Hit(e.Location); card.Cursor = a!=null?Cursors.Hand:Cursors.Hand; }; listPanel.Controls.Add(card); } if(!q.Any()){ var empty = new Label { Text = "אין פריטים להצגה.\nהעתק משהו בחלונות — והוא יופיע כאן ✦", ForeColor = Theme.Sub, Font = Theme.F(11f), TextAlign = ContentAlignment.MiddleCenter, Height = 160, Width = listPanel.ClientSize.Width-40, RightToLeft = RightToLeft.Yes }; listPanel.Controls.Add(empty); } listPanel.ResumeLayout(); listPanel.PerformLayout(); } void DoAction(ClipItem it, string action){ try { switch(action){ case "copy": SetClip(it.Text); Toast("הועתק ללוח"); break; case "open": System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(Norm(it.Text)){ UseShellExecute = true }); break; case "maps": { var m = Regex.Match(it.Text, @"^(-?\d{1,3}\.\d{3,}),\s*(-?\d{1,3}\.\d{3,})$"); string url = m.Success ? $"https://www.google.com/maps?q={m.Groups[1].Value},{m.Groups[2].Value}" : $"https://www.google.com/maps/search/{Uri.EscapeDataString(it.Text)}"; System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url){ UseShellExecute = true }); break; } case "mail": System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo($"mailto:{it.Text}"){ UseShellExecute = true }); break; case "call": System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo($"tel:{Clean(it.Text)}"){ UseShellExecute = true }); break; case "wa": System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo($"https://wa.me/{Clean(it.Text).TrimStart('+')}"){ UseShellExecute = true }); break; case "explore": System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{it.Text}\""); break; case "calc": { var num = new string(it.Text.Where(c=>char.IsDigit(c)||c=='.'||c=='-').ToArray()); if(double.TryParse(num, out var v)) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo($"calc.exe"){ UseShellExecute=true }).WaitForExit(0); Toast("נפתח המחשבון עבור " + v.ToString("N2")); break; } case "pin": it.Pinned = !it.Pinned; History.Save(items); RefreshList(); break; case "del": items.RemoveAll(x=> x.Id==it.Id); History.Save(items); RefreshList(); break; } } catch(Exception ex){ Toast("שגיאה: " + ex.Message); } } static string Norm(string u)=> u.StartsWith("http",StringComparison.OrdinalIgnoreCase)?u:"https://"+u; static string Clean(string s)=> new string(s.Where(c=>char.IsDigit(c)||c=='+').ToArray()); Bubble toastB; void Toast(string msg){ if(toastB != null) toastB.Close(); toastB = new Bubble(msg); toastB.Show(this); } protected override void OnShown(EventArgs e){ base.OnShown(e); BuildTray(); try { RegisterHotKey(Handle, (int)hotkeyId, 0x0002|0x0008, 0x4E); } catch {} } protected override void WndProc(ref Message m){ if(m.Msg == 0x0312){ ShowMe(); } base.WndProc(ref m); } void BuildTray(){ tray = new NotifyIcon { Icon = Icon, Visible = true, Text = "ניצוץ — מנהל לוח הגזירים" }; var menu = new ContextMenuStrip{ RightToLeft = RightToLeft.Yes, Font = Theme.F(10f) }; var show = new ToolStripMenuItem("פתח את ניצוץ"); show.Click += (s,e)=> ShowMe(); var clear = new ToolStripMenuItem("נקה היסטוריה"); clear.Click += (s,e)=>{ History.Clear(); items.Clear(); RefreshList(); }; var exit = new ToolStripMenuItem("יציאה"); exit.Click += (s,e)=>{ tray.Visible=false; Application.Exit(); }; menu.Items.Add(show); menu.Items.Add(new ToolStripSeparator()); menu.Items.Add(clear); menu.Items.Add(exit); tray.ContextMenuStrip = menu; tray.DoubleClick += (s,e)=> ShowMe(); } void ShowMe(){ Show(); WindowState = FormWindowState.Normal; BringToFront(); Activate(); items = History.Load(); RefreshList(); } protected override void OnFormClosing(FormClosingEventArgs e){ if(e.CloseReason == CloseReason.UserClosing){ e.Cancel = true; Hide(); tray.ShowBalloonTip(1500, "ניצוץ ממשיך לפעול ברקע", "Ctrl+Shift+N לפתיחה · לחץ פעמיים על האייקון במגש", ToolTipIcon.Info); } base.OnFormClosing(e); } protected override void OnFormClosed(FormClosedEventArgs e){ try{ UnregisterHotKey(Handle,(int)hotkeyId); }catch{} } } class Bubble : Form { Timer t; public Bubble(string msg){ FormBorderStyle = FormBorderStyle.None; StartPosition = FormStartPosition.Manual; Size = new Size(280, 52); BackColor = Theme.Bg2; ShowInTaskbar = false; TopMost = true; RightToLeft = RightToLeft.Yes; var lbl = new Label { Dock = DockStyle.Fill, Text = "✦ " + msg, ForeColor = Theme.Accent, TextAlign = ContentAlignment.MiddleCenter, Font = Theme.F(11f, FontStyle.Bold) }; Controls.Add(lbl); var scr = Screen.PrimaryScreen.WorkingArea; Location = new Point(scr.Right - Width - 24, scr.Bottom - Height - 24); t = new Timer{ Interval = 1800 }; t.Tick += (s,e)=>{ t.Stop(); Close(); }; } protected override void OnShown(EventArgs e){ base.OnShown(e); t.Start(); } protected override CreateParams CreateParams { get { var cp = base.CreateParams; cp.ExStyle |= 0x08000000; cp.ClassStyle |= 0x00020000; return cp; } } } static class Program { [STAThread] static void Main(){ ApplicationConfiguration.Initialize(); if(System.Diagnostics.Process.GetProcessesByName("Nitzotz").Length > 1){ return; } Application.Run(new MainForm()); } } } '@ Set-Content "$root\Program.cs" $app -Encoding UTF8 # ---------- 5. מחולל אייקון ---------- Say 'יוצר אייקון מקורי...' Yellow $iconProj = @' <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0-windows</TargetFramework> <UseWindowsForms>true</UseWindowsForms> <Nullable>disable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> </Project> '@ Set-Content "$tmp\IconGen.csproj" $iconProj -Encoding UTF8 Copy-Item "$root\IconGen.cs" "$tmp\IconGen.cs" -Force Push-Location $tmp & dotnet run -c Release -- "$root\app.ico" 2>&1 | Out-String | ForEach-Object { if($_ -match 'ICON_OK'){ Say 'האייקון נוצר בהצלחה ✦' Green } } Pop-Location if(-not (Test-Path "$root\app.ico")){ Say 'יצירת האייקון נכשלה, ממשיך ללא אייקון מותאם.' Yellow } # ---------- 6. קומפילציה ל-EXE ---------- Say 'מקמפל EXE עצמאי (זה לוקח 1-3 דקות)...' Cyan Set-Location $root & dotnet publish -c Release -r win-x64 --self-contained true ` -p:PublishSingleFile=true ` -p:IncludeNativeLibrariesForSelfExtract=true ` -p:PublishTrimmed=false ` -p:DebugType=none ` -o "$root\publish" 2>&1 | Out-String | ForEach-Object { if($_ -match 'error'){ Write-Host $_ -ForegroundColor Red } } $exe = Join-Path $root 'publish\Nitzotz.exe' if(-not (Test-Path $exe)){ throw 'הקומפילציה נכשלה. הרץ שוב או בדוק שהאינטרנט פעיל.' } Copy-Item $exe (Join-Path $out 'ניצוץ.exe') -Force if(Test-Path "$root\app.ico"){ Copy-Item "$root\app.ico" (Join-Path $out 'app.ico') -Force } # ---------- 7. ניקוי והצגת התוצאה ---------- Remove-Item $root,$tmp -Recurse -Force -ErrorAction SilentlyContinue $final = Join-Path $out 'ניצוץ.exe' $size = [math]::Round((Get-Item $final).Length/1MB,1) Write-Host "" Write-Host " ╔══════════════════════════════════════════════════════╗" -ForegroundColor Green Write-Host " ║ ✦ ניצוץ נבנה בהצלחה! ║" -ForegroundColor Green Write-Host " ╚══════════════════════════════════════════════════════╝" -ForegroundColor Green Write-Host "" Write-Host " הקובץ: ניצוץ.exe ($size MB)" -ForegroundColor White Write-Host " מיקום: $out" -ForegroundColor White Write-Host "" Write-Host " פתח את התוכנה בלחיצה כפולה. היא רצה ברקע ותופסת כל העתקה." -ForegroundColor Cyan Write-Host " קיצור גלובלי לפתיחה: Ctrl + Shift + N" -ForegroundColor Cyan Write-Host " סגירת החלון = המשך עבודה במגש המערכת (אייקון ליד השעון)." -ForegroundColor DarkGray Write-Host "" $open = Read-Host " לפתוח את התיקייה? (Y/N)" if($open -match '^[Yy]'){ Start-Process explorer.exe $out }המקום הרבעי גיפיטי
אני עדיין מחכה
️עריכה:
שם התוכנה: „נובה” — מרכז השליטה החכם ל-Windows.
נובה יושבת ברקע ונפתחת ב־Ctrl+Space: חיפוש מיידי של תוכנות וקבצים, היסטוריית לוח־העתקה, פעולות חכמות, פתיחת תיקיות/קישורים וחיפוש טבעי — הכול בממשק עברי מודרני.הקוד המלא [לא נבדק]
$ErrorActionPreference = "Stop" # ============================================================ # NOVA — נובה # Windows Smart Command Center # התקנה + יצירת קבצים + אייקון + Build ל-EXE בלחיצה אחת # ============================================================ $Base = Join-Path $env:USERPROFILE "Desktop\NOVA" $Src = Join-Path $Base "src" $Out = Join-Path $Base "NOVA" $Py = Join-Path $Src "nova.py" $Icon = Join-Path $Src "nova.ico" Write-Host "" Write-Host "=============================================" -ForegroundColor Cyan Write-Host " NOVA / נובה" -ForegroundColor Cyan Write-Host " מרכז השליטה החכם ל-Windows" -ForegroundColor White Write-Host "=============================================" -ForegroundColor Cyan Write-Host "" # ------------------------------------------------------------ # מציאת Python # ------------------------------------------------------------ $Python = $null $Candidates = @( "py", "python", "python3" ) foreach ($cmd in $Candidates) { try { $test = & $cmd --version 2>$null if ($LASTEXITCODE -eq 0 -and $test -match "Python 3") { $Python = $cmd break } } catch {} } if (-not $Python) { Write-Host "לא נמצא Python במחשב." -ForegroundColor Red Write-Host "" Write-Host "הסקריפט מנסה להתקין Python באופן אוטומטי..." -ForegroundColor Yellow try { winget --version | Out-Null winget install --id Python.Python.3.12 -e --source winget ` --accept-package-agreements ` --accept-source-agreements $Python = "py" } catch { Write-Host "" Write-Host "לא ניתן להתקין Python באופן אוטומטי." -ForegroundColor Red Write-Host "יש להתקין Python 3.11 ומעלה ואז להריץ את הבלוק שוב." -ForegroundColor Yellow exit 1 } } Write-Host "Python נמצא: $Python" -ForegroundColor Green # ------------------------------------------------------------ # יצירת תיקיות # ------------------------------------------------------------ New-Item -ItemType Directory -Force -Path $Base | Out-Null New-Item -ItemType Directory -Force -Path $Src | Out-Null New-Item -ItemType Directory -Force -Path $Out | Out-Null # ------------------------------------------------------------ # יצירת קובץ התוכנה # ------------------------------------------------------------ @' import sys import os import re import json import time import ctypes import shutil import subprocess import threading from pathlib import Path from urllib.parse import quote from PySide6.QtCore import ( Qt, QTimer, Signal, QObject, QThread, QSize, QPoint ) from PySide6.QtGui import ( QAction, QIcon, QPixmap, QPainter, QColor, QFont, QKeySequence, QShortcut ) from PySide6.QtWidgets import ( QApplication, QWidget, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QPushButton, QFrame, QSystemTrayIcon, QMenu, QMessageBox, QStackedWidget, QGraphicsDropShadowEffect ) APP_NAME = "נובה" APP_VERSION = "1.0.0" APPDATA = Path(os.environ.get("APPDATA", Path.home())) DATA_DIR = APPDATA / "NOVA" DATA_DIR.mkdir(parents=True, exist_ok=True) CLIP_FILE = DATA_DIR / "clipboard.json" MAX_CLIPS = 60 MAX_RESULTS = 40 # ============================================================ # עזרי Windows # ============================================================ user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 HOTKEY_ID = 7351 WM_HOTKEY = 0x0312 MOD_CONTROL = 0x0002 MOD_SHIFT = 0x0004 MOD_ALT = 0x0001 VK_SPACE = 0x20 def register_global_hotkey(): try: user32.RegisterHotKey(None, HOTKEY_ID, MOD_CONTROL, VK_SPACE) return True except Exception: return False def unregister_global_hotkey(): try: user32.UnregisterHotKey(None, HOTKEY_ID) except Exception: pass def open_target(target): try: os.startfile(str(target)) return True except Exception: try: subprocess.Popen([str(target)]) return True except Exception: return False def open_url(url): try: os.startfile(url) return True except Exception: return False # ============================================================ # Clipboard # ============================================================ class ClipboardStore: def __init__(self): self.items = [] self.load() def load(self): try: if CLIP_FILE.exists(): data = json.loads(CLIP_FILE.read_text(encoding="utf-8")) if isinstance(data, list): self.items = data[:MAX_CLIPS] except Exception: self.items = [] def save(self): try: CLIP_FILE.write_text( json.dumps(self.items, ensure_ascii=False, indent=2), encoding="utf-8" ) except Exception: pass def add(self, text): text = text.strip() if not text: return False if len(text) > 10000: text = text[:10000] if self.items and self.items[0] == text: return False self.items = [x for x in self.items if x != text] self.items.insert(0, text) self.items = self.items[:MAX_CLIPS] self.save() return True # ============================================================ # אינדקס תוכנות # ============================================================ class SearchItem: def __init__(self, name, path, kind="file", extra=""): self.name = name self.path = path self.kind = kind self.extra = extra class SearchIndex(QObject): ready = Signal() def __init__(self): super().__init__() self.items = [] self.lock = threading.Lock() def build(self): thread = threading.Thread( target=self._build, daemon=True ) thread.start() def _add(self, name, path, kind, extra=""): if not name or not path: return self.items.append( SearchItem(name, path, kind, extra) ) def _build(self): result = [] start_locations = [] appdata = os.environ.get("APPDATA") programdata = os.environ.get("PROGRAMDATA") userprofile = os.environ.get("USERPROFILE") if appdata: start_locations.append( Path(appdata) / "Microsoft/Windows/Start Menu/Programs" ) if programdata: start_locations.append( Path(programdata) / "Microsoft/Windows/Start Menu/Programs" ) desktop = Path(userprofile) / "Desktop" # תוכנות מתפריט התחל for root in start_locations: try: if not root.exists(): continue for p in root.rglob("*.lnk"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass for p in root.rglob("*.exe"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass except Exception: pass # קיצורי דרך בשולחן העבודה try: if desktop.exists(): for p in desktop.iterdir(): if p.suffix.lower() in (".lnk", ".exe", ".url"): result.append( SearchItem( p.stem, str(p), "app" if p.suffix.lower() != ".url" else "url", "שולחן העבודה" ) ) except Exception: pass # תיקיות נפוצות folders = [ ("שולחן העבודה", Path(userprofile) / "Desktop"), ("הורדות", Path(userprofile) / "Downloads"), ("מסמכים", Path(userprofile) / "Documents"), ("תמונות", Path(userprofile) / "Pictures"), ("וידאו", Path(userprofile) / "Videos"), ] allowed = { ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".csv", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".mp4", ".mkv", ".avi", ".mp3", ".wav", ".zip", ".rar", ".7z", ".py", ".ps1", ".bat", ".json" } for label, folder in folders: try: if not folder.exists(): continue count = 0 for p in folder.rglob("*"): try: if not p.is_file(): continue if p.suffix.lower() not in allowed: continue result.append( SearchItem( p.name, str(p), "file", label ) ) count += 1 # מונע אינדקס עצום במחשבים עמוסים if count >= 3500: break except Exception: pass except Exception: pass # הסרת כפילויות unique = {} for item in result: key = item.path.lower() if key not in unique: unique[key] = item with self.lock: self.items = list(unique.values()) self.ready.emit() # ============================================================ # חיפוש חכם # ============================================================ def score_text(query, text): q = query.lower().strip() t = text.lower() if not q: return 0 if t == q: return 1000 if t.startswith(q): return 800 if q in t: return 600 # התאמה לפי מילים words = [x for x in re.split(r"\s+", q) if x] score = 0 for word in words: if word in t: score += 150 # התאמת אותיות עוקבות pos = 0 matched = 0 for char in q: idx = t.find(char, pos) if idx >= 0: matched += 1 pos = idx + 1 if matched: score += int((matched / len(q)) * 100) return score # ============================================================ # כרטיס תוצאה # ============================================================ class ResultRow(QFrame): clicked = Signal(object) def __init__(self, item, parent=None): super().__init__(parent) self.item = item self.setObjectName("resultRow") self.setCursor(Qt.PointingHandCursor) layout = QHBoxLayout(self) layout.setContentsMargins(16, 11, 16, 11) layout.setSpacing(12) icon = QLabel() icon_text = { "app": "▣", "file": "▤", "url": "↗", "action": "✦", "clipboard": "⧉" }.get(item.kind, "•") icon.setText(icon_text) icon.setObjectName("resultIcon") icon.setFixedWidth(30) text_box = QVBoxLayout() text_box.setSpacing(2) title = QLabel(item.name) title.setObjectName("resultTitle") subtitle = QLabel(item.extra or item.path) subtitle.setObjectName("resultSub") subtitle.setTextInteractionFlags(Qt.NoTextInteraction) text_box.addWidget(title) text_box.addWidget(subtitle) layout.addWidget(icon) layout.addLayout(text_box, 1) arrow = QLabel("‹") arrow.setObjectName("resultArrow") layout.addWidget(arrow) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.clicked.emit(self.item) super().mousePressEvent(event) # ============================================================ # התוכנה # ============================================================ class NovaWindow(QMainWindow): def __init__(self): super().__init__() self.clipboard_store = ClipboardStore() self.index = SearchIndex() self.index.ready.connect(self.index_ready) self.all_results = [] self.selected_index = 0 self.hotkey_registered = False self.setWindowTitle("נובה — מרכז השליטה") self.setWindowIcon(QIcon(str(Path(sys.executable).parent / "nova.ico"))) self.setMinimumSize(760, 560) self.resize(860, 650) self.setAttribute(Qt.WA_DeleteOnClose, False) self.build_ui() self.install_global_hotkey_handler() self.index.build() # מעקב לוח self.clipboard_timer = QTimer(self) self.clipboard_timer.timeout.connect(self.check_clipboard) self.clipboard_timer.start(450) # עדכון סטטוס self.status_timer = QTimer(self) self.status_timer.timeout.connect(self.update_status) self.status_timer.start(1000) self.search_edit.setFocus() # -------------------------------------------------------- # UI # -------------------------------------------------------- def build_ui(self): root = QWidget() root.setObjectName("root") self.setCentralWidget(root) main = QVBoxLayout(root) main.setContentsMargins(28, 25, 28, 20) main.setSpacing(16) # Header header = QHBoxLayout() brand_box = QVBoxLayout() brand_box.setSpacing(0) brand = QLabel("נובה") brand.setObjectName("brand") subtitle = QLabel("מרכז השליטה החכם שלך") subtitle.setObjectName("subtitle") brand_box.addWidget(brand) brand_box.addWidget(subtitle) header.addLayout(brand_box) header.addStretch() status = QLabel("● פעילה") status.setObjectName("onlineStatus") header.addWidget(status) main.addLayout(header) # Search search_frame = QFrame() search_frame.setObjectName("searchFrame") search_layout = QHBoxLayout(search_frame) search_layout.setContentsMargins(18, 5, 18, 5) search_icon = QLabel("⌕") search_icon.setObjectName("searchIcon") self.search_edit = QLineEdit() self.search_edit.setPlaceholderText( "חפש תוכנה, קובץ, פעולה או כתובת..." ) self.search_edit.setClearButtonEnabled(True) self.search_edit.setObjectName("searchEdit") search_layout.addWidget(search_icon) search_layout.addWidget(self.search_edit) main.addWidget(search_frame) self.search_edit.textChanged.connect(self.perform_search) self.search_edit.returnPressed.connect(self.activate_selected) # Hint hint = QLabel( "Ctrl+Space לפתיחה מכל מקום • Enter לפתיחה • Esc להסתרה" ) hint.setObjectName("hint") hint.setAlignment(Qt.AlignCenter) main.addWidget(hint) # Content content = QHBoxLayout() content.setSpacing(14) # Results result_panel = QFrame() result_panel.setObjectName("panel") result_layout = QVBoxLayout(result_panel) result_layout.setContentsMargins(15, 15, 15, 15) result_layout.setSpacing(9) title_row = QHBoxLayout() self.results_title = QLabel("גישה מהירה") self.results_title.setObjectName("sectionTitle") self.results_count = QLabel("") self.results_count.setObjectName("count") title_row.addWidget(self.results_title) title_row.addStretch() title_row.addWidget(self.results_count) result_layout.addLayout(title_row) self.results = QVBoxLayout() self.results.setSpacing(7) result_layout.addLayout(self.results) result_layout.addStretch() content.addWidget(result_panel, 3) # Sidebar side = QFrame() side.setObjectName("sidePanel") side_layout = QVBoxLayout(side) side_layout.setContentsMargins(14, 14, 14, 14) side_layout.setSpacing(9) side_title = QLabel("פעולות מהירות") side_title.setObjectName("sectionTitle") side_layout.addWidget(side_title) actions = [ ("📋", "לוח ההעתקות", self.show_clipboard), ("📁", "פתח הורדות", self.open_downloads), ("🖥", "מחשב זה", self.open_computer), ("⚙", "הגדרות Windows", self.open_settings), ("🌐", "חיפוש באינטרנט", self.web_search), ] for icon, text, callback in actions: btn = QPushButton(f"{icon} {text}") btn.setObjectName("actionButton") btn.setCursor(Qt.PointingHandCursor) btn.clicked.connect(callback) side_layout.addWidget(btn) side_layout.addStretch() info = QLabel( "נובה עובדת ברקע.\n" "אין צורך להשאיר את החלון פתוח.\n\n" "הנתונים המקומיים נשמרים\n" "במחשב שלך." ) info.setObjectName("info") info.setWordWrap(True) side_layout.addWidget(info) content.addWidget(side, 1) main.addLayout(content, 1) # Footer footer = QHBoxLayout() self.status_label = QLabel("מאתחל אינדקס...") self.status_label.setObjectName("footer") footer.addWidget(self.status_label) footer.addStretch() version = QLabel(f"נובה {APP_VERSION}") version.setObjectName("footer") footer.addWidget(version) main.addLayout(footer) self.apply_style() # -------------------------------------------------------- # Style # -------------------------------------------------------- def apply_style(self): self.setStyleSheet(""" * { font-family: "Segoe UI"; } QMainWindow, QWidget#root { background: #0b1020; color: #edf2ff; } QLabel { color: #edf2ff; } QLabel#brand { font-size: 31px; font-weight: 800; color: #ffffff; } QLabel#subtitle { font-size: 13px; color: #8792ad; } QLabel#onlineStatus { background: #112a25; color: #55e6b1; border: 1px solid #1c5547; border-radius: 15px; padding: 6px 12px; font-size: 12px; font-weight: 600; } QFrame#searchFrame { background: #141b31; border: 1px solid #273253; border-radius: 17px; } QFrame#searchFrame:focus-within { border: 1px solid #6077ff; } QLabel#searchIcon { font-size: 29px; color: #7185ff; padding-bottom: 2px; } QLineEdit#searchEdit { background: transparent; border: none; color: #ffffff; font-size: 18px; padding: 11px 4px; selection-background-color: #4f61d8; } QLabel#hint { color: #68738e; font-size: 11px; } QFrame#panel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QFrame#sidePanel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QLabel#sectionTitle { color: #ffffff; font-size: 14px; font-weight: 700; } QLabel#count { color: #68738e; font-size: 11px; } QFrame#resultRow { background: #151d33; border: 1px solid transparent; border-radius: 12px; } QFrame#resultRow:hover { background: #1b2642; border: 1px solid #34436b; } QLabel#resultIcon { color: #7185ff; font-size: 21px; font-weight: bold; } QLabel#resultTitle { color: #f4f6ff; font-size: 13px; font-weight: 600; } QLabel#resultSub { color: #687690; font-size: 10px; } QLabel#resultArrow { color: #52607d; font-size: 21px; } QPushButton#actionButton { text-align: right; background: #151d33; border: 1px solid #222e4a; border-radius: 11px; color: #dbe2f6; padding: 12px 11px; font-size: 12px; } QPushButton#actionButton:hover { background: #1d2948; border: 1px solid #394b7a; } QPushButton#actionButton:pressed { background: #11182b; } QLabel#info { color: #626e89; background: #0d1426; border-radius: 10px; padding: 12px; font-size: 10px; } QLabel#footer { color: #59657f; font-size: 10px; } """) # -------------------------------------------------------- # חיפוש # -------------------------------------------------------- def clear_results(self): while self.results.count(): item = self.results.takeAt(0) widget = item.widget() if widget: widget.deleteLater() def perform_search(self, query): query = query.strip() self.clear_results() if not query: self.results_title.setText("גישה מהירה") quick = [ SearchItem( "לוח ההעתקות", "", "clipboard", "העתקות אחרונות" ), SearchItem( "פתח הורדות", "", "action", "תיקיית ההורדות" ), SearchItem( "מחשב זה", "", "action", "סייר הקבצים" ), SearchItem( "הגדרות Windows", "", "action", "הגדרות מערכת" ) ] self.all_results = quick for item in quick: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText("גישה מהירה") return self.results_title.setText("תוצאות") results = [] # פקודות חכמות normalized = query.lower() if normalized.startswith("פתח "): target = query[5:].strip() candidates = [ ("הורדות", Path.home() / "Downloads"), ("מסמכים", Path.home() / "Documents"), ("שולחן העבודה", Path.home() / "Desktop"), ("תמונות", Path.home() / "Pictures"), ("וידאו", Path.home() / "Videos"), ] for name, path in candidates: if target in name or name in target: results.append( SearchItem( f"פתח {name}", str(path), "action", str(path) ) ) # URL if re.match(r"^(https?://|www\.)", query, re.I): url = query if url.startswith("www."): url = "https://" + url results.append( SearchItem( "פתח כתובת", url, "url", url ) ) # חיפוש באינטרנט if normalized.startswith("חפש "): text = query[5:].strip() if text: url = ( "https://www.google.com/search?q=" + quote(text) ) results.append( SearchItem( "חפש באינטרנט", url, "url", text ) ) # לוח העתקות if normalized in ( "לוח", "העתקות", "לוח העתקות", "clipboard" ): for i, text in enumerate(self.clipboard_store.items[:10]): short = text.replace("\n", " ") if len(short) > 80: short = short[:80] + "..." results.append( SearchItem( short, text, "clipboard", "לחץ כדי להעתיק" ) ) # אינדקס with self.index.lock: indexed = list(self.index.items) scored = [] for item in indexed: s = max( score_text(query, item.name), score_text(query, Path(item.path).name) ) if s > 0: scored.append((s, item)) scored.sort( key=lambda x: ( -x[0], x[1].name.lower() ) ) results.extend( [item for _, item in scored[:MAX_RESULTS]] ) self.all_results = results[:MAX_RESULTS] if not self.all_results: empty = QLabel( "לא מצאתי תוצאה מתאימה.\n\n" "אפשר לנסות שם תוכנה, שם קובץ,\n" "כתובת אינטרנט או \"חפש ...\"." ) empty.setAlignment(Qt.AlignCenter) empty.setObjectName("info") empty.setMinimumHeight(130) self.results.addWidget(empty) self.results_count.setText("0 תוצאות") return for item in self.all_results: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText( f"{len(self.all_results)} תוצאות" ) # -------------------------------------------------------- # פעולות # -------------------------------------------------------- def activate_selected(self): if self.all_results: self.activate_item(self.all_results[0]) def activate_item(self, item): if item.kind == "clipboard": QApplication.clipboard().setText(item.path) self.status_label.setText("הטקסט הועתק ללוח") self.hide() return if item.kind == "action": if item.name == "פתח הורדות": self.open_downloads() elif item.name == "מחשב זה": self.open_computer() elif item.name == "הגדרות Windows": self.open_settings() elif item.name == "לוח ההעתקות": self.show_clipboard() else: open_target(item.path) return if item.kind == "url": open_url(item.path) self.hide() return if item.kind in ("app", "file"): open_target(item.path) self.hide() return open_target(item.path) self.hide() def show_clipboard(self): self.search_edit.setText("לוח") self.search_edit.setFocus() self.show() self.raise_() self.activateWindow() def open_downloads(self): open_target(Path.home() / "Downloads") self.hide() def open_computer(self): try: subprocess.Popen("explorer.exe shell:MyComputerFolder") except Exception: pass self.hide() def open_settings(self): try: subprocess.Popen( "start ms-settings:", shell=True ) except Exception: pass self.hide() def web_search(self): text = self.search_edit.text().strip() if not text: text = "חיפוש" url = ( "https://www.google.com/search?q=" + quote(text) ) open_url(url) self.hide() # -------------------------------------------------------- # Clipboard monitor # -------------------------------------------------------- def check_clipboard(self): try: text = QApplication.clipboard().text() if text and text.strip(): changed = self.clipboard_store.add(text) if changed and self.isVisible(): if self.search_edit.text().strip() == "לוח": self.perform_search("לוח") except Exception: pass # -------------------------------------------------------- # אינדקס # -------------------------------------------------------- def index_ready(self): count = len(self.index.items) self.status_label.setText( f"האינדקס מוכן • {count:,} פריטים זמינים לחיפוש" ) if not self.search_edit.text().strip(): self.perform_search("") def update_status(self): if not self.index.items: self.status_label.setText("בונה אינדקס...") # -------------------------------------------------------- # Global Hotkey # -------------------------------------------------------- def install_global_hotkey_handler(self): self.hotkey_registered = register_global_hotkey() timer = QTimer(self) timer.timeout.connect(self.poll_windows_messages) timer.start(80) self.hotkey_timer = timer def poll_windows_messages(self): msg = ctypes.wintypes.MSG() try: while user32.PeekMessageW( ctypes.byref(msg), None, WM_HOTKEY, WM_HOTKEY, 1 ): if msg.message == WM_HOTKEY: self.toggle_window() except Exception: pass def toggle_window(self): if self.isVisible() and self.isActiveWindow(): self.hide() return self.show() self.raise_() self.activateWindow() self.search_edit.setFocus() self.search_edit.selectAll() # -------------------------------------------------------- # Keyboard # -------------------------------------------------------- def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() event.accept() return if event.key() in ( Qt.Key_Return, Qt.Key_Enter ): self.activate_selected() event.accept() return super().keyPressEvent(event) # -------------------------------------------------------- # Close = hide to tray # -------------------------------------------------------- def closeEvent(self, event): event.ignore() self.hide() def cleanup(self): unregister_global_hotkey() # ============================================================ # System Tray # ============================================================ def create_tray(app, window): icon_path = Path(sys.executable).parent / "nova.ico" tray = QSystemTrayIcon() if icon_path.exists(): tray.setIcon(QIcon(str(icon_path))) else: tray.setIcon(app.style().standardIcon( app.style().SP_ComputerIcon )) tray.setToolTip("נובה — מרכז השליטה החכם") menu = QMenu() open_action = QAction("פתח את נובה", menu) open_action.triggered.connect(window.toggle_window) menu.addAction(open_action) menu.addSeparator() quit_action = QAction("יציאה מנובה", menu) def quit_app(): window.cleanup() app.quit() quit_action.triggered.connect(quit_app) menu.addAction(quit_action) tray.setContextMenu(menu) tray.activated.connect( lambda reason: ( window.toggle_window() if reason == QSystemTrayIcon.DoubleClick else None ) ) tray.show() return tray # ============================================================ # Main # ============================================================ def main(): QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) app = QApplication(sys.argv) app.setApplicationName("נובה") app.setApplicationDisplayName("נובה — מרכז השליטה החכם") app.setQuitOnLastWindowClosed(False) window = NovaWindow() tray = create_tray(app, window) # מתחילים פתוח בפעם הראשונה window.show() window.raise_() window.activateWindow() exit_code = app.exec() window.cleanup() sys.exit(exit_code) if __name__ == "__main__": main() '@ | Set-Content -Path $Py -Encoding UTF8 # ------------------------------------------------------------ # יצירת אייקון מקצועי באמצעות Pillow # ------------------------------------------------------------ @' from PIL import Image, ImageDraw sizes = [16, 24, 32, 48, 64, 128, 256] images = [] for size in sizes: img = Image.new("RGBA", (size, size), (11, 16, 32, 255)) d = ImageDraw.Draw(img) # רקע מעוגל margin = max(1, size // 16) d.rounded_rectangle( (margin, margin, size-margin, size-margin), radius=max(3, size//5), fill=(20, 28, 50, 255), outline=(91, 112, 255, 255), width=max(1, size//32) ) # סמל N / כוכב טכנולוגי cx = size // 2 cy = size // 2 pts = [ (cx, int(size*0.17)), (int(size*0.66), int(size*0.39)), (int(size*0.84), int(size*0.30)), (int(size*0.73), int(size*0.55)), (int(size*0.83), int(size*0.80)), (int(size*0.62), int(size*0.64)), (int(size*0.38), int(size*0.83)), (int(size*0.26), int(size*0.59)), (int(size*0.16), int(size*0.70)), (int(size*0.27), int(size*0.43)), ] d.polygon( pts, fill=(106, 126, 255, 255) ) # מרכז זוהר r = max(1, size // 11) d.ellipse( (cx-r, cy-r, cx+r, cy+r), fill=(236, 240, 255, 255) ) images.append(img) images[0].save( "nova.ico", format="ICO", sizes=[(s, s) for s in sizes], append_images=images[1:] ) '@ | Set-Content -Path (Join-Path $Src "make_icon.py") -Encoding UTF8 # ------------------------------------------------------------ # התקנת Build dependencies # ------------------------------------------------------------ Write-Host "" Write-Host "מתקין את מנוע הממשק ואת כלי הבנייה..." -ForegroundColor Yellow & $Python -m pip install --upgrade pip --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "עדכון pip נכשל." } & $Python -m pip install PySide6 Pillow PyInstaller --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "התקנת התלויות נכשלה." } # ------------------------------------------------------------ # יצירת אייקון # ------------------------------------------------------------ Write-Host "יוצר אייקון..." -ForegroundColor Yellow Push-Location $Src & $Python "make_icon.py" if ($LASTEXITCODE -ne 0) { Pop-Location throw "יצירת האייקון נכשלה." } Pop-Location # ------------------------------------------------------------ # Build # ------------------------------------------------------------ Write-Host "" Write-Host "בונה את NOVA.EXE..." -ForegroundColor Yellow Write-Host "זה השלב הארוך ביותר." -ForegroundColor DarkGray # ניקוי Build קודם $BuildDir = Join-Path $Src "build" $SpecFile = Join-Path $Src "nova.spec" if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force } if (Test-Path $SpecFile) { Remove-Item $SpecFile -Force } if (Test-Path $Out) { Get-ChildItem $Out -Force | Remove-Item -Recurse -Force } Push-Location $Src & $Python -m PyInstaller ` --noconfirm ` --clean ` --onefile ` --windowed ` --name "NOVA" ` --icon "nova.ico" ` --add-data "nova.ico;." ` "nova.py" $BuildExit = $LASTEXITCODE Pop-Location if ($BuildExit -ne 0) { throw "בניית ה-EXE נכשלה." } # ------------------------------------------------------------ # העתקה לתיקיית התוצאה # ------------------------------------------------------------ $BuiltExe = Join-Path $Src "dist\NOVA.exe" $FinalExe = Join-Path $Out "NOVA.exe" if (-not (Test-Path $BuiltExe)) { throw "ה-EXE לא נוצר." } Copy-Item $BuiltExe $FinalExe -Force # ------------------------------------------------------------ # יצירת README # ------------------------------------------------------------ @' נובה — מרכז השליטה החכם ======================== קובץ ההפעלה: NOVA.exe הפעלה: לחיצה כפולה על NOVA.exe קיצור דרך גלובלי: Ctrl + Space מה נובה יודעת לעשות: • חיפוש תוכנות • חיפוש קבצים בתיקיות נפוצות • פתיחת קבצים ותוכנות • פתיחת תיקיות Windows • היסטוריית לוח העתקות • חיפוש באינטרנט • פתיחת כתובות אינטרנט • עבודה ברקע דרך אזור ההתראות • קיצור דרך גלובלי לפתיחה מכל מקום הנתונים נשמרים מקומית במחשב. אין צורך להתקין Python כדי להריץ את NOVA.exe. '@ | Set-Content -Path (Join-Path $Out "README.txt") -Encoding UTF8 # ------------------------------------------------------------ # יצירת קיצור דרך על שולחן העבודה # ------------------------------------------------------------ try { $WshShell = New-Object -ComObject WScript.Shell $ShortcutPath = Join-Path ` ([Environment]::GetFolderPath("Desktop")) ` "נובה.lnk" $Shortcut = $WshShell.CreateShortcut($ShortcutPath) $Shortcut.TargetPath = $FinalExe $Shortcut.WorkingDirectory = $Out $Shortcut.IconLocation = "$FinalExe,0" $Shortcut.Description = "נובה — מרכז השליטה החכם" $Shortcut.Save() } catch { Write-Host "לא ניתן היה ליצור קיצור דרך אוטומטי." -ForegroundColor DarkYellow } # ------------------------------------------------------------ # סיום # ------------------------------------------------------------ $SizeMB = [math]::Round( (Get-Item $FinalExe).Length / 1MB, 1 ) Write-Host "" Write-Host "=============================================" -ForegroundColor Green Write-Host " הבנייה הסתיימה!" -ForegroundColor Green Write-Host "=============================================" -ForegroundColor Green Write-Host "" Write-Host "ה-EXE המוכן נמצא כאן:" -ForegroundColor White Write-Host $FinalExe -ForegroundColor Cyan Write-Host "" Write-Host "גודל: $SizeMB MB" -ForegroundColor Gray Write-Host "" Write-Host "נוצר גם קיצור דרך 'נובה' על שולחן העבודה." -ForegroundColor Green Write-Host "" Write-Host "הפעל עכשיו את NOVA.exe." -ForegroundColor White Write-Host "" Write-Host "קיצור הדרך של התוכנה: Ctrl + Space" -ForegroundColor Cyan Write-Host "" # פתיחת תיקיית התוצאה Start-Process explorer.exe -ArgumentList "`"$Out`"" # הפעלת התוכנה Start-Process -FilePath $FinalExeאבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים? -
המקום הרבעי גיפיטי
אני עדיין מחכה
️עריכה:
שם התוכנה: „נובה” — מרכז השליטה החכם ל-Windows.
נובה יושבת ברקע ונפתחת ב־Ctrl+Space: חיפוש מיידי של תוכנות וקבצים, היסטוריית לוח־העתקה, פעולות חכמות, פתיחת תיקיות/קישורים וחיפוש טבעי — הכול בממשק עברי מודרני.הקוד המלא [לא נבדק]
$ErrorActionPreference = "Stop" # ============================================================ # NOVA — נובה # Windows Smart Command Center # התקנה + יצירת קבצים + אייקון + Build ל-EXE בלחיצה אחת # ============================================================ $Base = Join-Path $env:USERPROFILE "Desktop\NOVA" $Src = Join-Path $Base "src" $Out = Join-Path $Base "NOVA" $Py = Join-Path $Src "nova.py" $Icon = Join-Path $Src "nova.ico" Write-Host "" Write-Host "=============================================" -ForegroundColor Cyan Write-Host " NOVA / נובה" -ForegroundColor Cyan Write-Host " מרכז השליטה החכם ל-Windows" -ForegroundColor White Write-Host "=============================================" -ForegroundColor Cyan Write-Host "" # ------------------------------------------------------------ # מציאת Python # ------------------------------------------------------------ $Python = $null $Candidates = @( "py", "python", "python3" ) foreach ($cmd in $Candidates) { try { $test = & $cmd --version 2>$null if ($LASTEXITCODE -eq 0 -and $test -match "Python 3") { $Python = $cmd break } } catch {} } if (-not $Python) { Write-Host "לא נמצא Python במחשב." -ForegroundColor Red Write-Host "" Write-Host "הסקריפט מנסה להתקין Python באופן אוטומטי..." -ForegroundColor Yellow try { winget --version | Out-Null winget install --id Python.Python.3.12 -e --source winget ` --accept-package-agreements ` --accept-source-agreements $Python = "py" } catch { Write-Host "" Write-Host "לא ניתן להתקין Python באופן אוטומטי." -ForegroundColor Red Write-Host "יש להתקין Python 3.11 ומעלה ואז להריץ את הבלוק שוב." -ForegroundColor Yellow exit 1 } } Write-Host "Python נמצא: $Python" -ForegroundColor Green # ------------------------------------------------------------ # יצירת תיקיות # ------------------------------------------------------------ New-Item -ItemType Directory -Force -Path $Base | Out-Null New-Item -ItemType Directory -Force -Path $Src | Out-Null New-Item -ItemType Directory -Force -Path $Out | Out-Null # ------------------------------------------------------------ # יצירת קובץ התוכנה # ------------------------------------------------------------ @' import sys import os import re import json import time import ctypes import shutil import subprocess import threading from pathlib import Path from urllib.parse import quote from PySide6.QtCore import ( Qt, QTimer, Signal, QObject, QThread, QSize, QPoint ) from PySide6.QtGui import ( QAction, QIcon, QPixmap, QPainter, QColor, QFont, QKeySequence, QShortcut ) from PySide6.QtWidgets import ( QApplication, QWidget, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QPushButton, QFrame, QSystemTrayIcon, QMenu, QMessageBox, QStackedWidget, QGraphicsDropShadowEffect ) APP_NAME = "נובה" APP_VERSION = "1.0.0" APPDATA = Path(os.environ.get("APPDATA", Path.home())) DATA_DIR = APPDATA / "NOVA" DATA_DIR.mkdir(parents=True, exist_ok=True) CLIP_FILE = DATA_DIR / "clipboard.json" MAX_CLIPS = 60 MAX_RESULTS = 40 # ============================================================ # עזרי Windows # ============================================================ user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 HOTKEY_ID = 7351 WM_HOTKEY = 0x0312 MOD_CONTROL = 0x0002 MOD_SHIFT = 0x0004 MOD_ALT = 0x0001 VK_SPACE = 0x20 def register_global_hotkey(): try: user32.RegisterHotKey(None, HOTKEY_ID, MOD_CONTROL, VK_SPACE) return True except Exception: return False def unregister_global_hotkey(): try: user32.UnregisterHotKey(None, HOTKEY_ID) except Exception: pass def open_target(target): try: os.startfile(str(target)) return True except Exception: try: subprocess.Popen([str(target)]) return True except Exception: return False def open_url(url): try: os.startfile(url) return True except Exception: return False # ============================================================ # Clipboard # ============================================================ class ClipboardStore: def __init__(self): self.items = [] self.load() def load(self): try: if CLIP_FILE.exists(): data = json.loads(CLIP_FILE.read_text(encoding="utf-8")) if isinstance(data, list): self.items = data[:MAX_CLIPS] except Exception: self.items = [] def save(self): try: CLIP_FILE.write_text( json.dumps(self.items, ensure_ascii=False, indent=2), encoding="utf-8" ) except Exception: pass def add(self, text): text = text.strip() if not text: return False if len(text) > 10000: text = text[:10000] if self.items and self.items[0] == text: return False self.items = [x for x in self.items if x != text] self.items.insert(0, text) self.items = self.items[:MAX_CLIPS] self.save() return True # ============================================================ # אינדקס תוכנות # ============================================================ class SearchItem: def __init__(self, name, path, kind="file", extra=""): self.name = name self.path = path self.kind = kind self.extra = extra class SearchIndex(QObject): ready = Signal() def __init__(self): super().__init__() self.items = [] self.lock = threading.Lock() def build(self): thread = threading.Thread( target=self._build, daemon=True ) thread.start() def _add(self, name, path, kind, extra=""): if not name or not path: return self.items.append( SearchItem(name, path, kind, extra) ) def _build(self): result = [] start_locations = [] appdata = os.environ.get("APPDATA") programdata = os.environ.get("PROGRAMDATA") userprofile = os.environ.get("USERPROFILE") if appdata: start_locations.append( Path(appdata) / "Microsoft/Windows/Start Menu/Programs" ) if programdata: start_locations.append( Path(programdata) / "Microsoft/Windows/Start Menu/Programs" ) desktop = Path(userprofile) / "Desktop" # תוכנות מתפריט התחל for root in start_locations: try: if not root.exists(): continue for p in root.rglob("*.lnk"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass for p in root.rglob("*.exe"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass except Exception: pass # קיצורי דרך בשולחן העבודה try: if desktop.exists(): for p in desktop.iterdir(): if p.suffix.lower() in (".lnk", ".exe", ".url"): result.append( SearchItem( p.stem, str(p), "app" if p.suffix.lower() != ".url" else "url", "שולחן העבודה" ) ) except Exception: pass # תיקיות נפוצות folders = [ ("שולחן העבודה", Path(userprofile) / "Desktop"), ("הורדות", Path(userprofile) / "Downloads"), ("מסמכים", Path(userprofile) / "Documents"), ("תמונות", Path(userprofile) / "Pictures"), ("וידאו", Path(userprofile) / "Videos"), ] allowed = { ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".csv", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".mp4", ".mkv", ".avi", ".mp3", ".wav", ".zip", ".rar", ".7z", ".py", ".ps1", ".bat", ".json" } for label, folder in folders: try: if not folder.exists(): continue count = 0 for p in folder.rglob("*"): try: if not p.is_file(): continue if p.suffix.lower() not in allowed: continue result.append( SearchItem( p.name, str(p), "file", label ) ) count += 1 # מונע אינדקס עצום במחשבים עמוסים if count >= 3500: break except Exception: pass except Exception: pass # הסרת כפילויות unique = {} for item in result: key = item.path.lower() if key not in unique: unique[key] = item with self.lock: self.items = list(unique.values()) self.ready.emit() # ============================================================ # חיפוש חכם # ============================================================ def score_text(query, text): q = query.lower().strip() t = text.lower() if not q: return 0 if t == q: return 1000 if t.startswith(q): return 800 if q in t: return 600 # התאמה לפי מילים words = [x for x in re.split(r"\s+", q) if x] score = 0 for word in words: if word in t: score += 150 # התאמת אותיות עוקבות pos = 0 matched = 0 for char in q: idx = t.find(char, pos) if idx >= 0: matched += 1 pos = idx + 1 if matched: score += int((matched / len(q)) * 100) return score # ============================================================ # כרטיס תוצאה # ============================================================ class ResultRow(QFrame): clicked = Signal(object) def __init__(self, item, parent=None): super().__init__(parent) self.item = item self.setObjectName("resultRow") self.setCursor(Qt.PointingHandCursor) layout = QHBoxLayout(self) layout.setContentsMargins(16, 11, 16, 11) layout.setSpacing(12) icon = QLabel() icon_text = { "app": "▣", "file": "▤", "url": "↗", "action": "✦", "clipboard": "⧉" }.get(item.kind, "•") icon.setText(icon_text) icon.setObjectName("resultIcon") icon.setFixedWidth(30) text_box = QVBoxLayout() text_box.setSpacing(2) title = QLabel(item.name) title.setObjectName("resultTitle") subtitle = QLabel(item.extra or item.path) subtitle.setObjectName("resultSub") subtitle.setTextInteractionFlags(Qt.NoTextInteraction) text_box.addWidget(title) text_box.addWidget(subtitle) layout.addWidget(icon) layout.addLayout(text_box, 1) arrow = QLabel("‹") arrow.setObjectName("resultArrow") layout.addWidget(arrow) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.clicked.emit(self.item) super().mousePressEvent(event) # ============================================================ # התוכנה # ============================================================ class NovaWindow(QMainWindow): def __init__(self): super().__init__() self.clipboard_store = ClipboardStore() self.index = SearchIndex() self.index.ready.connect(self.index_ready) self.all_results = [] self.selected_index = 0 self.hotkey_registered = False self.setWindowTitle("נובה — מרכז השליטה") self.setWindowIcon(QIcon(str(Path(sys.executable).parent / "nova.ico"))) self.setMinimumSize(760, 560) self.resize(860, 650) self.setAttribute(Qt.WA_DeleteOnClose, False) self.build_ui() self.install_global_hotkey_handler() self.index.build() # מעקב לוח self.clipboard_timer = QTimer(self) self.clipboard_timer.timeout.connect(self.check_clipboard) self.clipboard_timer.start(450) # עדכון סטטוס self.status_timer = QTimer(self) self.status_timer.timeout.connect(self.update_status) self.status_timer.start(1000) self.search_edit.setFocus() # -------------------------------------------------------- # UI # -------------------------------------------------------- def build_ui(self): root = QWidget() root.setObjectName("root") self.setCentralWidget(root) main = QVBoxLayout(root) main.setContentsMargins(28, 25, 28, 20) main.setSpacing(16) # Header header = QHBoxLayout() brand_box = QVBoxLayout() brand_box.setSpacing(0) brand = QLabel("נובה") brand.setObjectName("brand") subtitle = QLabel("מרכז השליטה החכם שלך") subtitle.setObjectName("subtitle") brand_box.addWidget(brand) brand_box.addWidget(subtitle) header.addLayout(brand_box) header.addStretch() status = QLabel("● פעילה") status.setObjectName("onlineStatus") header.addWidget(status) main.addLayout(header) # Search search_frame = QFrame() search_frame.setObjectName("searchFrame") search_layout = QHBoxLayout(search_frame) search_layout.setContentsMargins(18, 5, 18, 5) search_icon = QLabel("⌕") search_icon.setObjectName("searchIcon") self.search_edit = QLineEdit() self.search_edit.setPlaceholderText( "חפש תוכנה, קובץ, פעולה או כתובת..." ) self.search_edit.setClearButtonEnabled(True) self.search_edit.setObjectName("searchEdit") search_layout.addWidget(search_icon) search_layout.addWidget(self.search_edit) main.addWidget(search_frame) self.search_edit.textChanged.connect(self.perform_search) self.search_edit.returnPressed.connect(self.activate_selected) # Hint hint = QLabel( "Ctrl+Space לפתיחה מכל מקום • Enter לפתיחה • Esc להסתרה" ) hint.setObjectName("hint") hint.setAlignment(Qt.AlignCenter) main.addWidget(hint) # Content content = QHBoxLayout() content.setSpacing(14) # Results result_panel = QFrame() result_panel.setObjectName("panel") result_layout = QVBoxLayout(result_panel) result_layout.setContentsMargins(15, 15, 15, 15) result_layout.setSpacing(9) title_row = QHBoxLayout() self.results_title = QLabel("גישה מהירה") self.results_title.setObjectName("sectionTitle") self.results_count = QLabel("") self.results_count.setObjectName("count") title_row.addWidget(self.results_title) title_row.addStretch() title_row.addWidget(self.results_count) result_layout.addLayout(title_row) self.results = QVBoxLayout() self.results.setSpacing(7) result_layout.addLayout(self.results) result_layout.addStretch() content.addWidget(result_panel, 3) # Sidebar side = QFrame() side.setObjectName("sidePanel") side_layout = QVBoxLayout(side) side_layout.setContentsMargins(14, 14, 14, 14) side_layout.setSpacing(9) side_title = QLabel("פעולות מהירות") side_title.setObjectName("sectionTitle") side_layout.addWidget(side_title) actions = [ ("📋", "לוח ההעתקות", self.show_clipboard), ("📁", "פתח הורדות", self.open_downloads), ("🖥", "מחשב זה", self.open_computer), ("⚙", "הגדרות Windows", self.open_settings), ("🌐", "חיפוש באינטרנט", self.web_search), ] for icon, text, callback in actions: btn = QPushButton(f"{icon} {text}") btn.setObjectName("actionButton") btn.setCursor(Qt.PointingHandCursor) btn.clicked.connect(callback) side_layout.addWidget(btn) side_layout.addStretch() info = QLabel( "נובה עובדת ברקע.\n" "אין צורך להשאיר את החלון פתוח.\n\n" "הנתונים המקומיים נשמרים\n" "במחשב שלך." ) info.setObjectName("info") info.setWordWrap(True) side_layout.addWidget(info) content.addWidget(side, 1) main.addLayout(content, 1) # Footer footer = QHBoxLayout() self.status_label = QLabel("מאתחל אינדקס...") self.status_label.setObjectName("footer") footer.addWidget(self.status_label) footer.addStretch() version = QLabel(f"נובה {APP_VERSION}") version.setObjectName("footer") footer.addWidget(version) main.addLayout(footer) self.apply_style() # -------------------------------------------------------- # Style # -------------------------------------------------------- def apply_style(self): self.setStyleSheet(""" * { font-family: "Segoe UI"; } QMainWindow, QWidget#root { background: #0b1020; color: #edf2ff; } QLabel { color: #edf2ff; } QLabel#brand { font-size: 31px; font-weight: 800; color: #ffffff; } QLabel#subtitle { font-size: 13px; color: #8792ad; } QLabel#onlineStatus { background: #112a25; color: #55e6b1; border: 1px solid #1c5547; border-radius: 15px; padding: 6px 12px; font-size: 12px; font-weight: 600; } QFrame#searchFrame { background: #141b31; border: 1px solid #273253; border-radius: 17px; } QFrame#searchFrame:focus-within { border: 1px solid #6077ff; } QLabel#searchIcon { font-size: 29px; color: #7185ff; padding-bottom: 2px; } QLineEdit#searchEdit { background: transparent; border: none; color: #ffffff; font-size: 18px; padding: 11px 4px; selection-background-color: #4f61d8; } QLabel#hint { color: #68738e; font-size: 11px; } QFrame#panel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QFrame#sidePanel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QLabel#sectionTitle { color: #ffffff; font-size: 14px; font-weight: 700; } QLabel#count { color: #68738e; font-size: 11px; } QFrame#resultRow { background: #151d33; border: 1px solid transparent; border-radius: 12px; } QFrame#resultRow:hover { background: #1b2642; border: 1px solid #34436b; } QLabel#resultIcon { color: #7185ff; font-size: 21px; font-weight: bold; } QLabel#resultTitle { color: #f4f6ff; font-size: 13px; font-weight: 600; } QLabel#resultSub { color: #687690; font-size: 10px; } QLabel#resultArrow { color: #52607d; font-size: 21px; } QPushButton#actionButton { text-align: right; background: #151d33; border: 1px solid #222e4a; border-radius: 11px; color: #dbe2f6; padding: 12px 11px; font-size: 12px; } QPushButton#actionButton:hover { background: #1d2948; border: 1px solid #394b7a; } QPushButton#actionButton:pressed { background: #11182b; } QLabel#info { color: #626e89; background: #0d1426; border-radius: 10px; padding: 12px; font-size: 10px; } QLabel#footer { color: #59657f; font-size: 10px; } """) # -------------------------------------------------------- # חיפוש # -------------------------------------------------------- def clear_results(self): while self.results.count(): item = self.results.takeAt(0) widget = item.widget() if widget: widget.deleteLater() def perform_search(self, query): query = query.strip() self.clear_results() if not query: self.results_title.setText("גישה מהירה") quick = [ SearchItem( "לוח ההעתקות", "", "clipboard", "העתקות אחרונות" ), SearchItem( "פתח הורדות", "", "action", "תיקיית ההורדות" ), SearchItem( "מחשב זה", "", "action", "סייר הקבצים" ), SearchItem( "הגדרות Windows", "", "action", "הגדרות מערכת" ) ] self.all_results = quick for item in quick: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText("גישה מהירה") return self.results_title.setText("תוצאות") results = [] # פקודות חכמות normalized = query.lower() if normalized.startswith("פתח "): target = query[5:].strip() candidates = [ ("הורדות", Path.home() / "Downloads"), ("מסמכים", Path.home() / "Documents"), ("שולחן העבודה", Path.home() / "Desktop"), ("תמונות", Path.home() / "Pictures"), ("וידאו", Path.home() / "Videos"), ] for name, path in candidates: if target in name or name in target: results.append( SearchItem( f"פתח {name}", str(path), "action", str(path) ) ) # URL if re.match(r"^(https?://|www\.)", query, re.I): url = query if url.startswith("www."): url = "https://" + url results.append( SearchItem( "פתח כתובת", url, "url", url ) ) # חיפוש באינטרנט if normalized.startswith("חפש "): text = query[5:].strip() if text: url = ( "https://www.google.com/search?q=" + quote(text) ) results.append( SearchItem( "חפש באינטרנט", url, "url", text ) ) # לוח העתקות if normalized in ( "לוח", "העתקות", "לוח העתקות", "clipboard" ): for i, text in enumerate(self.clipboard_store.items[:10]): short = text.replace("\n", " ") if len(short) > 80: short = short[:80] + "..." results.append( SearchItem( short, text, "clipboard", "לחץ כדי להעתיק" ) ) # אינדקס with self.index.lock: indexed = list(self.index.items) scored = [] for item in indexed: s = max( score_text(query, item.name), score_text(query, Path(item.path).name) ) if s > 0: scored.append((s, item)) scored.sort( key=lambda x: ( -x[0], x[1].name.lower() ) ) results.extend( [item for _, item in scored[:MAX_RESULTS]] ) self.all_results = results[:MAX_RESULTS] if not self.all_results: empty = QLabel( "לא מצאתי תוצאה מתאימה.\n\n" "אפשר לנסות שם תוכנה, שם קובץ,\n" "כתובת אינטרנט או \"חפש ...\"." ) empty.setAlignment(Qt.AlignCenter) empty.setObjectName("info") empty.setMinimumHeight(130) self.results.addWidget(empty) self.results_count.setText("0 תוצאות") return for item in self.all_results: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText( f"{len(self.all_results)} תוצאות" ) # -------------------------------------------------------- # פעולות # -------------------------------------------------------- def activate_selected(self): if self.all_results: self.activate_item(self.all_results[0]) def activate_item(self, item): if item.kind == "clipboard": QApplication.clipboard().setText(item.path) self.status_label.setText("הטקסט הועתק ללוח") self.hide() return if item.kind == "action": if item.name == "פתח הורדות": self.open_downloads() elif item.name == "מחשב זה": self.open_computer() elif item.name == "הגדרות Windows": self.open_settings() elif item.name == "לוח ההעתקות": self.show_clipboard() else: open_target(item.path) return if item.kind == "url": open_url(item.path) self.hide() return if item.kind in ("app", "file"): open_target(item.path) self.hide() return open_target(item.path) self.hide() def show_clipboard(self): self.search_edit.setText("לוח") self.search_edit.setFocus() self.show() self.raise_() self.activateWindow() def open_downloads(self): open_target(Path.home() / "Downloads") self.hide() def open_computer(self): try: subprocess.Popen("explorer.exe shell:MyComputerFolder") except Exception: pass self.hide() def open_settings(self): try: subprocess.Popen( "start ms-settings:", shell=True ) except Exception: pass self.hide() def web_search(self): text = self.search_edit.text().strip() if not text: text = "חיפוש" url = ( "https://www.google.com/search?q=" + quote(text) ) open_url(url) self.hide() # -------------------------------------------------------- # Clipboard monitor # -------------------------------------------------------- def check_clipboard(self): try: text = QApplication.clipboard().text() if text and text.strip(): changed = self.clipboard_store.add(text) if changed and self.isVisible(): if self.search_edit.text().strip() == "לוח": self.perform_search("לוח") except Exception: pass # -------------------------------------------------------- # אינדקס # -------------------------------------------------------- def index_ready(self): count = len(self.index.items) self.status_label.setText( f"האינדקס מוכן • {count:,} פריטים זמינים לחיפוש" ) if not self.search_edit.text().strip(): self.perform_search("") def update_status(self): if not self.index.items: self.status_label.setText("בונה אינדקס...") # -------------------------------------------------------- # Global Hotkey # -------------------------------------------------------- def install_global_hotkey_handler(self): self.hotkey_registered = register_global_hotkey() timer = QTimer(self) timer.timeout.connect(self.poll_windows_messages) timer.start(80) self.hotkey_timer = timer def poll_windows_messages(self): msg = ctypes.wintypes.MSG() try: while user32.PeekMessageW( ctypes.byref(msg), None, WM_HOTKEY, WM_HOTKEY, 1 ): if msg.message == WM_HOTKEY: self.toggle_window() except Exception: pass def toggle_window(self): if self.isVisible() and self.isActiveWindow(): self.hide() return self.show() self.raise_() self.activateWindow() self.search_edit.setFocus() self.search_edit.selectAll() # -------------------------------------------------------- # Keyboard # -------------------------------------------------------- def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() event.accept() return if event.key() in ( Qt.Key_Return, Qt.Key_Enter ): self.activate_selected() event.accept() return super().keyPressEvent(event) # -------------------------------------------------------- # Close = hide to tray # -------------------------------------------------------- def closeEvent(self, event): event.ignore() self.hide() def cleanup(self): unregister_global_hotkey() # ============================================================ # System Tray # ============================================================ def create_tray(app, window): icon_path = Path(sys.executable).parent / "nova.ico" tray = QSystemTrayIcon() if icon_path.exists(): tray.setIcon(QIcon(str(icon_path))) else: tray.setIcon(app.style().standardIcon( app.style().SP_ComputerIcon )) tray.setToolTip("נובה — מרכז השליטה החכם") menu = QMenu() open_action = QAction("פתח את נובה", menu) open_action.triggered.connect(window.toggle_window) menu.addAction(open_action) menu.addSeparator() quit_action = QAction("יציאה מנובה", menu) def quit_app(): window.cleanup() app.quit() quit_action.triggered.connect(quit_app) menu.addAction(quit_action) tray.setContextMenu(menu) tray.activated.connect( lambda reason: ( window.toggle_window() if reason == QSystemTrayIcon.DoubleClick else None ) ) tray.show() return tray # ============================================================ # Main # ============================================================ def main(): QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) app = QApplication(sys.argv) app.setApplicationName("נובה") app.setApplicationDisplayName("נובה — מרכז השליטה החכם") app.setQuitOnLastWindowClosed(False) window = NovaWindow() tray = create_tray(app, window) # מתחילים פתוח בפעם הראשונה window.show() window.raise_() window.activateWindow() exit_code = app.exec() window.cleanup() sys.exit(exit_code) if __name__ == "__main__": main() '@ | Set-Content -Path $Py -Encoding UTF8 # ------------------------------------------------------------ # יצירת אייקון מקצועי באמצעות Pillow # ------------------------------------------------------------ @' from PIL import Image, ImageDraw sizes = [16, 24, 32, 48, 64, 128, 256] images = [] for size in sizes: img = Image.new("RGBA", (size, size), (11, 16, 32, 255)) d = ImageDraw.Draw(img) # רקע מעוגל margin = max(1, size // 16) d.rounded_rectangle( (margin, margin, size-margin, size-margin), radius=max(3, size//5), fill=(20, 28, 50, 255), outline=(91, 112, 255, 255), width=max(1, size//32) ) # סמל N / כוכב טכנולוגי cx = size // 2 cy = size // 2 pts = [ (cx, int(size*0.17)), (int(size*0.66), int(size*0.39)), (int(size*0.84), int(size*0.30)), (int(size*0.73), int(size*0.55)), (int(size*0.83), int(size*0.80)), (int(size*0.62), int(size*0.64)), (int(size*0.38), int(size*0.83)), (int(size*0.26), int(size*0.59)), (int(size*0.16), int(size*0.70)), (int(size*0.27), int(size*0.43)), ] d.polygon( pts, fill=(106, 126, 255, 255) ) # מרכז זוהר r = max(1, size // 11) d.ellipse( (cx-r, cy-r, cx+r, cy+r), fill=(236, 240, 255, 255) ) images.append(img) images[0].save( "nova.ico", format="ICO", sizes=[(s, s) for s in sizes], append_images=images[1:] ) '@ | Set-Content -Path (Join-Path $Src "make_icon.py") -Encoding UTF8 # ------------------------------------------------------------ # התקנת Build dependencies # ------------------------------------------------------------ Write-Host "" Write-Host "מתקין את מנוע הממשק ואת כלי הבנייה..." -ForegroundColor Yellow & $Python -m pip install --upgrade pip --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "עדכון pip נכשל." } & $Python -m pip install PySide6 Pillow PyInstaller --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "התקנת התלויות נכשלה." } # ------------------------------------------------------------ # יצירת אייקון # ------------------------------------------------------------ Write-Host "יוצר אייקון..." -ForegroundColor Yellow Push-Location $Src & $Python "make_icon.py" if ($LASTEXITCODE -ne 0) { Pop-Location throw "יצירת האייקון נכשלה." } Pop-Location # ------------------------------------------------------------ # Build # ------------------------------------------------------------ Write-Host "" Write-Host "בונה את NOVA.EXE..." -ForegroundColor Yellow Write-Host "זה השלב הארוך ביותר." -ForegroundColor DarkGray # ניקוי Build קודם $BuildDir = Join-Path $Src "build" $SpecFile = Join-Path $Src "nova.spec" if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force } if (Test-Path $SpecFile) { Remove-Item $SpecFile -Force } if (Test-Path $Out) { Get-ChildItem $Out -Force | Remove-Item -Recurse -Force } Push-Location $Src & $Python -m PyInstaller ` --noconfirm ` --clean ` --onefile ` --windowed ` --name "NOVA" ` --icon "nova.ico" ` --add-data "nova.ico;." ` "nova.py" $BuildExit = $LASTEXITCODE Pop-Location if ($BuildExit -ne 0) { throw "בניית ה-EXE נכשלה." } # ------------------------------------------------------------ # העתקה לתיקיית התוצאה # ------------------------------------------------------------ $BuiltExe = Join-Path $Src "dist\NOVA.exe" $FinalExe = Join-Path $Out "NOVA.exe" if (-not (Test-Path $BuiltExe)) { throw "ה-EXE לא נוצר." } Copy-Item $BuiltExe $FinalExe -Force # ------------------------------------------------------------ # יצירת README # ------------------------------------------------------------ @' נובה — מרכז השליטה החכם ======================== קובץ ההפעלה: NOVA.exe הפעלה: לחיצה כפולה על NOVA.exe קיצור דרך גלובלי: Ctrl + Space מה נובה יודעת לעשות: • חיפוש תוכנות • חיפוש קבצים בתיקיות נפוצות • פתיחת קבצים ותוכנות • פתיחת תיקיות Windows • היסטוריית לוח העתקות • חיפוש באינטרנט • פתיחת כתובות אינטרנט • עבודה ברקע דרך אזור ההתראות • קיצור דרך גלובלי לפתיחה מכל מקום הנתונים נשמרים מקומית במחשב. אין צורך להתקין Python כדי להריץ את NOVA.exe. '@ | Set-Content -Path (Join-Path $Out "README.txt") -Encoding UTF8 # ------------------------------------------------------------ # יצירת קיצור דרך על שולחן העבודה # ------------------------------------------------------------ try { $WshShell = New-Object -ComObject WScript.Shell $ShortcutPath = Join-Path ` ([Environment]::GetFolderPath("Desktop")) ` "נובה.lnk" $Shortcut = $WshShell.CreateShortcut($ShortcutPath) $Shortcut.TargetPath = $FinalExe $Shortcut.WorkingDirectory = $Out $Shortcut.IconLocation = "$FinalExe,0" $Shortcut.Description = "נובה — מרכז השליטה החכם" $Shortcut.Save() } catch { Write-Host "לא ניתן היה ליצור קיצור דרך אוטומטי." -ForegroundColor DarkYellow } # ------------------------------------------------------------ # סיום # ------------------------------------------------------------ $SizeMB = [math]::Round( (Get-Item $FinalExe).Length / 1MB, 1 ) Write-Host "" Write-Host "=============================================" -ForegroundColor Green Write-Host " הבנייה הסתיימה!" -ForegroundColor Green Write-Host "=============================================" -ForegroundColor Green Write-Host "" Write-Host "ה-EXE המוכן נמצא כאן:" -ForegroundColor White Write-Host $FinalExe -ForegroundColor Cyan Write-Host "" Write-Host "גודל: $SizeMB MB" -ForegroundColor Gray Write-Host "" Write-Host "נוצר גם קיצור דרך 'נובה' על שולחן העבודה." -ForegroundColor Green Write-Host "" Write-Host "הפעל עכשיו את NOVA.exe." -ForegroundColor White Write-Host "" Write-Host "קיצור הדרך של התוכנה: Ctrl + Space" -ForegroundColor Cyan Write-Host "" # פתיחת תיקיית התוצאה Start-Process explorer.exe -ArgumentList "`"$Out`"" # הפעלת התוכנה Start-Process -FilePath $FinalExeאבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים? -
@yahbdk איזה מודלים השתמשת בכל AI ובאיזה מצב?
גימני תמיד הכי מהיר גם באנטיגרויטי הוא הכי מהיר -
@ztcebuck
קלוד=אופוס 5
גימיני=3.5 Flash-Lite
פריבף=DeepSeek V4.1 Flash
גיפיטי=אני לא יודע איפה בודקים -
המקום הרבעי גיפיטי
אני עדיין מחכה
️עריכה:
שם התוכנה: „נובה” — מרכז השליטה החכם ל-Windows.
נובה יושבת ברקע ונפתחת ב־Ctrl+Space: חיפוש מיידי של תוכנות וקבצים, היסטוריית לוח־העתקה, פעולות חכמות, פתיחת תיקיות/קישורים וחיפוש טבעי — הכול בממשק עברי מודרני.הקוד המלא [לא נבדק]
$ErrorActionPreference = "Stop" # ============================================================ # NOVA — נובה # Windows Smart Command Center # התקנה + יצירת קבצים + אייקון + Build ל-EXE בלחיצה אחת # ============================================================ $Base = Join-Path $env:USERPROFILE "Desktop\NOVA" $Src = Join-Path $Base "src" $Out = Join-Path $Base "NOVA" $Py = Join-Path $Src "nova.py" $Icon = Join-Path $Src "nova.ico" Write-Host "" Write-Host "=============================================" -ForegroundColor Cyan Write-Host " NOVA / נובה" -ForegroundColor Cyan Write-Host " מרכז השליטה החכם ל-Windows" -ForegroundColor White Write-Host "=============================================" -ForegroundColor Cyan Write-Host "" # ------------------------------------------------------------ # מציאת Python # ------------------------------------------------------------ $Python = $null $Candidates = @( "py", "python", "python3" ) foreach ($cmd in $Candidates) { try { $test = & $cmd --version 2>$null if ($LASTEXITCODE -eq 0 -and $test -match "Python 3") { $Python = $cmd break } } catch {} } if (-not $Python) { Write-Host "לא נמצא Python במחשב." -ForegroundColor Red Write-Host "" Write-Host "הסקריפט מנסה להתקין Python באופן אוטומטי..." -ForegroundColor Yellow try { winget --version | Out-Null winget install --id Python.Python.3.12 -e --source winget ` --accept-package-agreements ` --accept-source-agreements $Python = "py" } catch { Write-Host "" Write-Host "לא ניתן להתקין Python באופן אוטומטי." -ForegroundColor Red Write-Host "יש להתקין Python 3.11 ומעלה ואז להריץ את הבלוק שוב." -ForegroundColor Yellow exit 1 } } Write-Host "Python נמצא: $Python" -ForegroundColor Green # ------------------------------------------------------------ # יצירת תיקיות # ------------------------------------------------------------ New-Item -ItemType Directory -Force -Path $Base | Out-Null New-Item -ItemType Directory -Force -Path $Src | Out-Null New-Item -ItemType Directory -Force -Path $Out | Out-Null # ------------------------------------------------------------ # יצירת קובץ התוכנה # ------------------------------------------------------------ @' import sys import os import re import json import time import ctypes import shutil import subprocess import threading from pathlib import Path from urllib.parse import quote from PySide6.QtCore import ( Qt, QTimer, Signal, QObject, QThread, QSize, QPoint ) from PySide6.QtGui import ( QAction, QIcon, QPixmap, QPainter, QColor, QFont, QKeySequence, QShortcut ) from PySide6.QtWidgets import ( QApplication, QWidget, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QPushButton, QFrame, QSystemTrayIcon, QMenu, QMessageBox, QStackedWidget, QGraphicsDropShadowEffect ) APP_NAME = "נובה" APP_VERSION = "1.0.0" APPDATA = Path(os.environ.get("APPDATA", Path.home())) DATA_DIR = APPDATA / "NOVA" DATA_DIR.mkdir(parents=True, exist_ok=True) CLIP_FILE = DATA_DIR / "clipboard.json" MAX_CLIPS = 60 MAX_RESULTS = 40 # ============================================================ # עזרי Windows # ============================================================ user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 HOTKEY_ID = 7351 WM_HOTKEY = 0x0312 MOD_CONTROL = 0x0002 MOD_SHIFT = 0x0004 MOD_ALT = 0x0001 VK_SPACE = 0x20 def register_global_hotkey(): try: user32.RegisterHotKey(None, HOTKEY_ID, MOD_CONTROL, VK_SPACE) return True except Exception: return False def unregister_global_hotkey(): try: user32.UnregisterHotKey(None, HOTKEY_ID) except Exception: pass def open_target(target): try: os.startfile(str(target)) return True except Exception: try: subprocess.Popen([str(target)]) return True except Exception: return False def open_url(url): try: os.startfile(url) return True except Exception: return False # ============================================================ # Clipboard # ============================================================ class ClipboardStore: def __init__(self): self.items = [] self.load() def load(self): try: if CLIP_FILE.exists(): data = json.loads(CLIP_FILE.read_text(encoding="utf-8")) if isinstance(data, list): self.items = data[:MAX_CLIPS] except Exception: self.items = [] def save(self): try: CLIP_FILE.write_text( json.dumps(self.items, ensure_ascii=False, indent=2), encoding="utf-8" ) except Exception: pass def add(self, text): text = text.strip() if not text: return False if len(text) > 10000: text = text[:10000] if self.items and self.items[0] == text: return False self.items = [x for x in self.items if x != text] self.items.insert(0, text) self.items = self.items[:MAX_CLIPS] self.save() return True # ============================================================ # אינדקס תוכנות # ============================================================ class SearchItem: def __init__(self, name, path, kind="file", extra=""): self.name = name self.path = path self.kind = kind self.extra = extra class SearchIndex(QObject): ready = Signal() def __init__(self): super().__init__() self.items = [] self.lock = threading.Lock() def build(self): thread = threading.Thread( target=self._build, daemon=True ) thread.start() def _add(self, name, path, kind, extra=""): if not name or not path: return self.items.append( SearchItem(name, path, kind, extra) ) def _build(self): result = [] start_locations = [] appdata = os.environ.get("APPDATA") programdata = os.environ.get("PROGRAMDATA") userprofile = os.environ.get("USERPROFILE") if appdata: start_locations.append( Path(appdata) / "Microsoft/Windows/Start Menu/Programs" ) if programdata: start_locations.append( Path(programdata) / "Microsoft/Windows/Start Menu/Programs" ) desktop = Path(userprofile) / "Desktop" # תוכנות מתפריט התחל for root in start_locations: try: if not root.exists(): continue for p in root.rglob("*.lnk"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass for p in root.rglob("*.exe"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass except Exception: pass # קיצורי דרך בשולחן העבודה try: if desktop.exists(): for p in desktop.iterdir(): if p.suffix.lower() in (".lnk", ".exe", ".url"): result.append( SearchItem( p.stem, str(p), "app" if p.suffix.lower() != ".url" else "url", "שולחן העבודה" ) ) except Exception: pass # תיקיות נפוצות folders = [ ("שולחן העבודה", Path(userprofile) / "Desktop"), ("הורדות", Path(userprofile) / "Downloads"), ("מסמכים", Path(userprofile) / "Documents"), ("תמונות", Path(userprofile) / "Pictures"), ("וידאו", Path(userprofile) / "Videos"), ] allowed = { ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".csv", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".mp4", ".mkv", ".avi", ".mp3", ".wav", ".zip", ".rar", ".7z", ".py", ".ps1", ".bat", ".json" } for label, folder in folders: try: if not folder.exists(): continue count = 0 for p in folder.rglob("*"): try: if not p.is_file(): continue if p.suffix.lower() not in allowed: continue result.append( SearchItem( p.name, str(p), "file", label ) ) count += 1 # מונע אינדקס עצום במחשבים עמוסים if count >= 3500: break except Exception: pass except Exception: pass # הסרת כפילויות unique = {} for item in result: key = item.path.lower() if key not in unique: unique[key] = item with self.lock: self.items = list(unique.values()) self.ready.emit() # ============================================================ # חיפוש חכם # ============================================================ def score_text(query, text): q = query.lower().strip() t = text.lower() if not q: return 0 if t == q: return 1000 if t.startswith(q): return 800 if q in t: return 600 # התאמה לפי מילים words = [x for x in re.split(r"\s+", q) if x] score = 0 for word in words: if word in t: score += 150 # התאמת אותיות עוקבות pos = 0 matched = 0 for char in q: idx = t.find(char, pos) if idx >= 0: matched += 1 pos = idx + 1 if matched: score += int((matched / len(q)) * 100) return score # ============================================================ # כרטיס תוצאה # ============================================================ class ResultRow(QFrame): clicked = Signal(object) def __init__(self, item, parent=None): super().__init__(parent) self.item = item self.setObjectName("resultRow") self.setCursor(Qt.PointingHandCursor) layout = QHBoxLayout(self) layout.setContentsMargins(16, 11, 16, 11) layout.setSpacing(12) icon = QLabel() icon_text = { "app": "▣", "file": "▤", "url": "↗", "action": "✦", "clipboard": "⧉" }.get(item.kind, "•") icon.setText(icon_text) icon.setObjectName("resultIcon") icon.setFixedWidth(30) text_box = QVBoxLayout() text_box.setSpacing(2) title = QLabel(item.name) title.setObjectName("resultTitle") subtitle = QLabel(item.extra or item.path) subtitle.setObjectName("resultSub") subtitle.setTextInteractionFlags(Qt.NoTextInteraction) text_box.addWidget(title) text_box.addWidget(subtitle) layout.addWidget(icon) layout.addLayout(text_box, 1) arrow = QLabel("‹") arrow.setObjectName("resultArrow") layout.addWidget(arrow) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.clicked.emit(self.item) super().mousePressEvent(event) # ============================================================ # התוכנה # ============================================================ class NovaWindow(QMainWindow): def __init__(self): super().__init__() self.clipboard_store = ClipboardStore() self.index = SearchIndex() self.index.ready.connect(self.index_ready) self.all_results = [] self.selected_index = 0 self.hotkey_registered = False self.setWindowTitle("נובה — מרכז השליטה") self.setWindowIcon(QIcon(str(Path(sys.executable).parent / "nova.ico"))) self.setMinimumSize(760, 560) self.resize(860, 650) self.setAttribute(Qt.WA_DeleteOnClose, False) self.build_ui() self.install_global_hotkey_handler() self.index.build() # מעקב לוח self.clipboard_timer = QTimer(self) self.clipboard_timer.timeout.connect(self.check_clipboard) self.clipboard_timer.start(450) # עדכון סטטוס self.status_timer = QTimer(self) self.status_timer.timeout.connect(self.update_status) self.status_timer.start(1000) self.search_edit.setFocus() # -------------------------------------------------------- # UI # -------------------------------------------------------- def build_ui(self): root = QWidget() root.setObjectName("root") self.setCentralWidget(root) main = QVBoxLayout(root) main.setContentsMargins(28, 25, 28, 20) main.setSpacing(16) # Header header = QHBoxLayout() brand_box = QVBoxLayout() brand_box.setSpacing(0) brand = QLabel("נובה") brand.setObjectName("brand") subtitle = QLabel("מרכז השליטה החכם שלך") subtitle.setObjectName("subtitle") brand_box.addWidget(brand) brand_box.addWidget(subtitle) header.addLayout(brand_box) header.addStretch() status = QLabel("● פעילה") status.setObjectName("onlineStatus") header.addWidget(status) main.addLayout(header) # Search search_frame = QFrame() search_frame.setObjectName("searchFrame") search_layout = QHBoxLayout(search_frame) search_layout.setContentsMargins(18, 5, 18, 5) search_icon = QLabel("⌕") search_icon.setObjectName("searchIcon") self.search_edit = QLineEdit() self.search_edit.setPlaceholderText( "חפש תוכנה, קובץ, פעולה או כתובת..." ) self.search_edit.setClearButtonEnabled(True) self.search_edit.setObjectName("searchEdit") search_layout.addWidget(search_icon) search_layout.addWidget(self.search_edit) main.addWidget(search_frame) self.search_edit.textChanged.connect(self.perform_search) self.search_edit.returnPressed.connect(self.activate_selected) # Hint hint = QLabel( "Ctrl+Space לפתיחה מכל מקום • Enter לפתיחה • Esc להסתרה" ) hint.setObjectName("hint") hint.setAlignment(Qt.AlignCenter) main.addWidget(hint) # Content content = QHBoxLayout() content.setSpacing(14) # Results result_panel = QFrame() result_panel.setObjectName("panel") result_layout = QVBoxLayout(result_panel) result_layout.setContentsMargins(15, 15, 15, 15) result_layout.setSpacing(9) title_row = QHBoxLayout() self.results_title = QLabel("גישה מהירה") self.results_title.setObjectName("sectionTitle") self.results_count = QLabel("") self.results_count.setObjectName("count") title_row.addWidget(self.results_title) title_row.addStretch() title_row.addWidget(self.results_count) result_layout.addLayout(title_row) self.results = QVBoxLayout() self.results.setSpacing(7) result_layout.addLayout(self.results) result_layout.addStretch() content.addWidget(result_panel, 3) # Sidebar side = QFrame() side.setObjectName("sidePanel") side_layout = QVBoxLayout(side) side_layout.setContentsMargins(14, 14, 14, 14) side_layout.setSpacing(9) side_title = QLabel("פעולות מהירות") side_title.setObjectName("sectionTitle") side_layout.addWidget(side_title) actions = [ ("📋", "לוח ההעתקות", self.show_clipboard), ("📁", "פתח הורדות", self.open_downloads), ("🖥", "מחשב זה", self.open_computer), ("⚙", "הגדרות Windows", self.open_settings), ("🌐", "חיפוש באינטרנט", self.web_search), ] for icon, text, callback in actions: btn = QPushButton(f"{icon} {text}") btn.setObjectName("actionButton") btn.setCursor(Qt.PointingHandCursor) btn.clicked.connect(callback) side_layout.addWidget(btn) side_layout.addStretch() info = QLabel( "נובה עובדת ברקע.\n" "אין צורך להשאיר את החלון פתוח.\n\n" "הנתונים המקומיים נשמרים\n" "במחשב שלך." ) info.setObjectName("info") info.setWordWrap(True) side_layout.addWidget(info) content.addWidget(side, 1) main.addLayout(content, 1) # Footer footer = QHBoxLayout() self.status_label = QLabel("מאתחל אינדקס...") self.status_label.setObjectName("footer") footer.addWidget(self.status_label) footer.addStretch() version = QLabel(f"נובה {APP_VERSION}") version.setObjectName("footer") footer.addWidget(version) main.addLayout(footer) self.apply_style() # -------------------------------------------------------- # Style # -------------------------------------------------------- def apply_style(self): self.setStyleSheet(""" * { font-family: "Segoe UI"; } QMainWindow, QWidget#root { background: #0b1020; color: #edf2ff; } QLabel { color: #edf2ff; } QLabel#brand { font-size: 31px; font-weight: 800; color: #ffffff; } QLabel#subtitle { font-size: 13px; color: #8792ad; } QLabel#onlineStatus { background: #112a25; color: #55e6b1; border: 1px solid #1c5547; border-radius: 15px; padding: 6px 12px; font-size: 12px; font-weight: 600; } QFrame#searchFrame { background: #141b31; border: 1px solid #273253; border-radius: 17px; } QFrame#searchFrame:focus-within { border: 1px solid #6077ff; } QLabel#searchIcon { font-size: 29px; color: #7185ff; padding-bottom: 2px; } QLineEdit#searchEdit { background: transparent; border: none; color: #ffffff; font-size: 18px; padding: 11px 4px; selection-background-color: #4f61d8; } QLabel#hint { color: #68738e; font-size: 11px; } QFrame#panel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QFrame#sidePanel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QLabel#sectionTitle { color: #ffffff; font-size: 14px; font-weight: 700; } QLabel#count { color: #68738e; font-size: 11px; } QFrame#resultRow { background: #151d33; border: 1px solid transparent; border-radius: 12px; } QFrame#resultRow:hover { background: #1b2642; border: 1px solid #34436b; } QLabel#resultIcon { color: #7185ff; font-size: 21px; font-weight: bold; } QLabel#resultTitle { color: #f4f6ff; font-size: 13px; font-weight: 600; } QLabel#resultSub { color: #687690; font-size: 10px; } QLabel#resultArrow { color: #52607d; font-size: 21px; } QPushButton#actionButton { text-align: right; background: #151d33; border: 1px solid #222e4a; border-radius: 11px; color: #dbe2f6; padding: 12px 11px; font-size: 12px; } QPushButton#actionButton:hover { background: #1d2948; border: 1px solid #394b7a; } QPushButton#actionButton:pressed { background: #11182b; } QLabel#info { color: #626e89; background: #0d1426; border-radius: 10px; padding: 12px; font-size: 10px; } QLabel#footer { color: #59657f; font-size: 10px; } """) # -------------------------------------------------------- # חיפוש # -------------------------------------------------------- def clear_results(self): while self.results.count(): item = self.results.takeAt(0) widget = item.widget() if widget: widget.deleteLater() def perform_search(self, query): query = query.strip() self.clear_results() if not query: self.results_title.setText("גישה מהירה") quick = [ SearchItem( "לוח ההעתקות", "", "clipboard", "העתקות אחרונות" ), SearchItem( "פתח הורדות", "", "action", "תיקיית ההורדות" ), SearchItem( "מחשב זה", "", "action", "סייר הקבצים" ), SearchItem( "הגדרות Windows", "", "action", "הגדרות מערכת" ) ] self.all_results = quick for item in quick: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText("גישה מהירה") return self.results_title.setText("תוצאות") results = [] # פקודות חכמות normalized = query.lower() if normalized.startswith("פתח "): target = query[5:].strip() candidates = [ ("הורדות", Path.home() / "Downloads"), ("מסמכים", Path.home() / "Documents"), ("שולחן העבודה", Path.home() / "Desktop"), ("תמונות", Path.home() / "Pictures"), ("וידאו", Path.home() / "Videos"), ] for name, path in candidates: if target in name or name in target: results.append( SearchItem( f"פתח {name}", str(path), "action", str(path) ) ) # URL if re.match(r"^(https?://|www\.)", query, re.I): url = query if url.startswith("www."): url = "https://" + url results.append( SearchItem( "פתח כתובת", url, "url", url ) ) # חיפוש באינטרנט if normalized.startswith("חפש "): text = query[5:].strip() if text: url = ( "https://www.google.com/search?q=" + quote(text) ) results.append( SearchItem( "חפש באינטרנט", url, "url", text ) ) # לוח העתקות if normalized in ( "לוח", "העתקות", "לוח העתקות", "clipboard" ): for i, text in enumerate(self.clipboard_store.items[:10]): short = text.replace("\n", " ") if len(short) > 80: short = short[:80] + "..." results.append( SearchItem( short, text, "clipboard", "לחץ כדי להעתיק" ) ) # אינדקס with self.index.lock: indexed = list(self.index.items) scored = [] for item in indexed: s = max( score_text(query, item.name), score_text(query, Path(item.path).name) ) if s > 0: scored.append((s, item)) scored.sort( key=lambda x: ( -x[0], x[1].name.lower() ) ) results.extend( [item for _, item in scored[:MAX_RESULTS]] ) self.all_results = results[:MAX_RESULTS] if not self.all_results: empty = QLabel( "לא מצאתי תוצאה מתאימה.\n\n" "אפשר לנסות שם תוכנה, שם קובץ,\n" "כתובת אינטרנט או \"חפש ...\"." ) empty.setAlignment(Qt.AlignCenter) empty.setObjectName("info") empty.setMinimumHeight(130) self.results.addWidget(empty) self.results_count.setText("0 תוצאות") return for item in self.all_results: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText( f"{len(self.all_results)} תוצאות" ) # -------------------------------------------------------- # פעולות # -------------------------------------------------------- def activate_selected(self): if self.all_results: self.activate_item(self.all_results[0]) def activate_item(self, item): if item.kind == "clipboard": QApplication.clipboard().setText(item.path) self.status_label.setText("הטקסט הועתק ללוח") self.hide() return if item.kind == "action": if item.name == "פתח הורדות": self.open_downloads() elif item.name == "מחשב זה": self.open_computer() elif item.name == "הגדרות Windows": self.open_settings() elif item.name == "לוח ההעתקות": self.show_clipboard() else: open_target(item.path) return if item.kind == "url": open_url(item.path) self.hide() return if item.kind in ("app", "file"): open_target(item.path) self.hide() return open_target(item.path) self.hide() def show_clipboard(self): self.search_edit.setText("לוח") self.search_edit.setFocus() self.show() self.raise_() self.activateWindow() def open_downloads(self): open_target(Path.home() / "Downloads") self.hide() def open_computer(self): try: subprocess.Popen("explorer.exe shell:MyComputerFolder") except Exception: pass self.hide() def open_settings(self): try: subprocess.Popen( "start ms-settings:", shell=True ) except Exception: pass self.hide() def web_search(self): text = self.search_edit.text().strip() if not text: text = "חיפוש" url = ( "https://www.google.com/search?q=" + quote(text) ) open_url(url) self.hide() # -------------------------------------------------------- # Clipboard monitor # -------------------------------------------------------- def check_clipboard(self): try: text = QApplication.clipboard().text() if text and text.strip(): changed = self.clipboard_store.add(text) if changed and self.isVisible(): if self.search_edit.text().strip() == "לוח": self.perform_search("לוח") except Exception: pass # -------------------------------------------------------- # אינדקס # -------------------------------------------------------- def index_ready(self): count = len(self.index.items) self.status_label.setText( f"האינדקס מוכן • {count:,} פריטים זמינים לחיפוש" ) if not self.search_edit.text().strip(): self.perform_search("") def update_status(self): if not self.index.items: self.status_label.setText("בונה אינדקס...") # -------------------------------------------------------- # Global Hotkey # -------------------------------------------------------- def install_global_hotkey_handler(self): self.hotkey_registered = register_global_hotkey() timer = QTimer(self) timer.timeout.connect(self.poll_windows_messages) timer.start(80) self.hotkey_timer = timer def poll_windows_messages(self): msg = ctypes.wintypes.MSG() try: while user32.PeekMessageW( ctypes.byref(msg), None, WM_HOTKEY, WM_HOTKEY, 1 ): if msg.message == WM_HOTKEY: self.toggle_window() except Exception: pass def toggle_window(self): if self.isVisible() and self.isActiveWindow(): self.hide() return self.show() self.raise_() self.activateWindow() self.search_edit.setFocus() self.search_edit.selectAll() # -------------------------------------------------------- # Keyboard # -------------------------------------------------------- def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() event.accept() return if event.key() in ( Qt.Key_Return, Qt.Key_Enter ): self.activate_selected() event.accept() return super().keyPressEvent(event) # -------------------------------------------------------- # Close = hide to tray # -------------------------------------------------------- def closeEvent(self, event): event.ignore() self.hide() def cleanup(self): unregister_global_hotkey() # ============================================================ # System Tray # ============================================================ def create_tray(app, window): icon_path = Path(sys.executable).parent / "nova.ico" tray = QSystemTrayIcon() if icon_path.exists(): tray.setIcon(QIcon(str(icon_path))) else: tray.setIcon(app.style().standardIcon( app.style().SP_ComputerIcon )) tray.setToolTip("נובה — מרכז השליטה החכם") menu = QMenu() open_action = QAction("פתח את נובה", menu) open_action.triggered.connect(window.toggle_window) menu.addAction(open_action) menu.addSeparator() quit_action = QAction("יציאה מנובה", menu) def quit_app(): window.cleanup() app.quit() quit_action.triggered.connect(quit_app) menu.addAction(quit_action) tray.setContextMenu(menu) tray.activated.connect( lambda reason: ( window.toggle_window() if reason == QSystemTrayIcon.DoubleClick else None ) ) tray.show() return tray # ============================================================ # Main # ============================================================ def main(): QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) app = QApplication(sys.argv) app.setApplicationName("נובה") app.setApplicationDisplayName("נובה — מרכז השליטה החכם") app.setQuitOnLastWindowClosed(False) window = NovaWindow() tray = create_tray(app, window) # מתחילים פתוח בפעם הראשונה window.show() window.raise_() window.activateWindow() exit_code = app.exec() window.cleanup() sys.exit(exit_code) if __name__ == "__main__": main() '@ | Set-Content -Path $Py -Encoding UTF8 # ------------------------------------------------------------ # יצירת אייקון מקצועי באמצעות Pillow # ------------------------------------------------------------ @' from PIL import Image, ImageDraw sizes = [16, 24, 32, 48, 64, 128, 256] images = [] for size in sizes: img = Image.new("RGBA", (size, size), (11, 16, 32, 255)) d = ImageDraw.Draw(img) # רקע מעוגל margin = max(1, size // 16) d.rounded_rectangle( (margin, margin, size-margin, size-margin), radius=max(3, size//5), fill=(20, 28, 50, 255), outline=(91, 112, 255, 255), width=max(1, size//32) ) # סמל N / כוכב טכנולוגי cx = size // 2 cy = size // 2 pts = [ (cx, int(size*0.17)), (int(size*0.66), int(size*0.39)), (int(size*0.84), int(size*0.30)), (int(size*0.73), int(size*0.55)), (int(size*0.83), int(size*0.80)), (int(size*0.62), int(size*0.64)), (int(size*0.38), int(size*0.83)), (int(size*0.26), int(size*0.59)), (int(size*0.16), int(size*0.70)), (int(size*0.27), int(size*0.43)), ] d.polygon( pts, fill=(106, 126, 255, 255) ) # מרכז זוהר r = max(1, size // 11) d.ellipse( (cx-r, cy-r, cx+r, cy+r), fill=(236, 240, 255, 255) ) images.append(img) images[0].save( "nova.ico", format="ICO", sizes=[(s, s) for s in sizes], append_images=images[1:] ) '@ | Set-Content -Path (Join-Path $Src "make_icon.py") -Encoding UTF8 # ------------------------------------------------------------ # התקנת Build dependencies # ------------------------------------------------------------ Write-Host "" Write-Host "מתקין את מנוע הממשק ואת כלי הבנייה..." -ForegroundColor Yellow & $Python -m pip install --upgrade pip --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "עדכון pip נכשל." } & $Python -m pip install PySide6 Pillow PyInstaller --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "התקנת התלויות נכשלה." } # ------------------------------------------------------------ # יצירת אייקון # ------------------------------------------------------------ Write-Host "יוצר אייקון..." -ForegroundColor Yellow Push-Location $Src & $Python "make_icon.py" if ($LASTEXITCODE -ne 0) { Pop-Location throw "יצירת האייקון נכשלה." } Pop-Location # ------------------------------------------------------------ # Build # ------------------------------------------------------------ Write-Host "" Write-Host "בונה את NOVA.EXE..." -ForegroundColor Yellow Write-Host "זה השלב הארוך ביותר." -ForegroundColor DarkGray # ניקוי Build קודם $BuildDir = Join-Path $Src "build" $SpecFile = Join-Path $Src "nova.spec" if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force } if (Test-Path $SpecFile) { Remove-Item $SpecFile -Force } if (Test-Path $Out) { Get-ChildItem $Out -Force | Remove-Item -Recurse -Force } Push-Location $Src & $Python -m PyInstaller ` --noconfirm ` --clean ` --onefile ` --windowed ` --name "NOVA" ` --icon "nova.ico" ` --add-data "nova.ico;." ` "nova.py" $BuildExit = $LASTEXITCODE Pop-Location if ($BuildExit -ne 0) { throw "בניית ה-EXE נכשלה." } # ------------------------------------------------------------ # העתקה לתיקיית התוצאה # ------------------------------------------------------------ $BuiltExe = Join-Path $Src "dist\NOVA.exe" $FinalExe = Join-Path $Out "NOVA.exe" if (-not (Test-Path $BuiltExe)) { throw "ה-EXE לא נוצר." } Copy-Item $BuiltExe $FinalExe -Force # ------------------------------------------------------------ # יצירת README # ------------------------------------------------------------ @' נובה — מרכז השליטה החכם ======================== קובץ ההפעלה: NOVA.exe הפעלה: לחיצה כפולה על NOVA.exe קיצור דרך גלובלי: Ctrl + Space מה נובה יודעת לעשות: • חיפוש תוכנות • חיפוש קבצים בתיקיות נפוצות • פתיחת קבצים ותוכנות • פתיחת תיקיות Windows • היסטוריית לוח העתקות • חיפוש באינטרנט • פתיחת כתובות אינטרנט • עבודה ברקע דרך אזור ההתראות • קיצור דרך גלובלי לפתיחה מכל מקום הנתונים נשמרים מקומית במחשב. אין צורך להתקין Python כדי להריץ את NOVA.exe. '@ | Set-Content -Path (Join-Path $Out "README.txt") -Encoding UTF8 # ------------------------------------------------------------ # יצירת קיצור דרך על שולחן העבודה # ------------------------------------------------------------ try { $WshShell = New-Object -ComObject WScript.Shell $ShortcutPath = Join-Path ` ([Environment]::GetFolderPath("Desktop")) ` "נובה.lnk" $Shortcut = $WshShell.CreateShortcut($ShortcutPath) $Shortcut.TargetPath = $FinalExe $Shortcut.WorkingDirectory = $Out $Shortcut.IconLocation = "$FinalExe,0" $Shortcut.Description = "נובה — מרכז השליטה החכם" $Shortcut.Save() } catch { Write-Host "לא ניתן היה ליצור קיצור דרך אוטומטי." -ForegroundColor DarkYellow } # ------------------------------------------------------------ # סיום # ------------------------------------------------------------ $SizeMB = [math]::Round( (Get-Item $FinalExe).Length / 1MB, 1 ) Write-Host "" Write-Host "=============================================" -ForegroundColor Green Write-Host " הבנייה הסתיימה!" -ForegroundColor Green Write-Host "=============================================" -ForegroundColor Green Write-Host "" Write-Host "ה-EXE המוכן נמצא כאן:" -ForegroundColor White Write-Host $FinalExe -ForegroundColor Cyan Write-Host "" Write-Host "גודל: $SizeMB MB" -ForegroundColor Gray Write-Host "" Write-Host "נוצר גם קיצור דרך 'נובה' על שולחן העבודה." -ForegroundColor Green Write-Host "" Write-Host "הפעל עכשיו את NOVA.exe." -ForegroundColor White Write-Host "" Write-Host "קיצור הדרך של התוכנה: Ctrl + Space" -ForegroundColor Cyan Write-Host "" # פתיחת תיקיית התוצאה Start-Process explorer.exe -ArgumentList "`"$Out`"" # הפעלת התוכנה Start-Process -FilePath $FinalExeאבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים? -
המקום הרבעי גיפיטי
אני עדיין מחכה
️עריכה:
שם התוכנה: „נובה” — מרכז השליטה החכם ל-Windows.
נובה יושבת ברקע ונפתחת ב־Ctrl+Space: חיפוש מיידי של תוכנות וקבצים, היסטוריית לוח־העתקה, פעולות חכמות, פתיחת תיקיות/קישורים וחיפוש טבעי — הכול בממשק עברי מודרני.הקוד המלא [לא נבדק]
$ErrorActionPreference = "Stop" # ============================================================ # NOVA — נובה # Windows Smart Command Center # התקנה + יצירת קבצים + אייקון + Build ל-EXE בלחיצה אחת # ============================================================ $Base = Join-Path $env:USERPROFILE "Desktop\NOVA" $Src = Join-Path $Base "src" $Out = Join-Path $Base "NOVA" $Py = Join-Path $Src "nova.py" $Icon = Join-Path $Src "nova.ico" Write-Host "" Write-Host "=============================================" -ForegroundColor Cyan Write-Host " NOVA / נובה" -ForegroundColor Cyan Write-Host " מרכז השליטה החכם ל-Windows" -ForegroundColor White Write-Host "=============================================" -ForegroundColor Cyan Write-Host "" # ------------------------------------------------------------ # מציאת Python # ------------------------------------------------------------ $Python = $null $Candidates = @( "py", "python", "python3" ) foreach ($cmd in $Candidates) { try { $test = & $cmd --version 2>$null if ($LASTEXITCODE -eq 0 -and $test -match "Python 3") { $Python = $cmd break } } catch {} } if (-not $Python) { Write-Host "לא נמצא Python במחשב." -ForegroundColor Red Write-Host "" Write-Host "הסקריפט מנסה להתקין Python באופן אוטומטי..." -ForegroundColor Yellow try { winget --version | Out-Null winget install --id Python.Python.3.12 -e --source winget ` --accept-package-agreements ` --accept-source-agreements $Python = "py" } catch { Write-Host "" Write-Host "לא ניתן להתקין Python באופן אוטומטי." -ForegroundColor Red Write-Host "יש להתקין Python 3.11 ומעלה ואז להריץ את הבלוק שוב." -ForegroundColor Yellow exit 1 } } Write-Host "Python נמצא: $Python" -ForegroundColor Green # ------------------------------------------------------------ # יצירת תיקיות # ------------------------------------------------------------ New-Item -ItemType Directory -Force -Path $Base | Out-Null New-Item -ItemType Directory -Force -Path $Src | Out-Null New-Item -ItemType Directory -Force -Path $Out | Out-Null # ------------------------------------------------------------ # יצירת קובץ התוכנה # ------------------------------------------------------------ @' import sys import os import re import json import time import ctypes import shutil import subprocess import threading from pathlib import Path from urllib.parse import quote from PySide6.QtCore import ( Qt, QTimer, Signal, QObject, QThread, QSize, QPoint ) from PySide6.QtGui import ( QAction, QIcon, QPixmap, QPainter, QColor, QFont, QKeySequence, QShortcut ) from PySide6.QtWidgets import ( QApplication, QWidget, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QPushButton, QFrame, QSystemTrayIcon, QMenu, QMessageBox, QStackedWidget, QGraphicsDropShadowEffect ) APP_NAME = "נובה" APP_VERSION = "1.0.0" APPDATA = Path(os.environ.get("APPDATA", Path.home())) DATA_DIR = APPDATA / "NOVA" DATA_DIR.mkdir(parents=True, exist_ok=True) CLIP_FILE = DATA_DIR / "clipboard.json" MAX_CLIPS = 60 MAX_RESULTS = 40 # ============================================================ # עזרי Windows # ============================================================ user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 HOTKEY_ID = 7351 WM_HOTKEY = 0x0312 MOD_CONTROL = 0x0002 MOD_SHIFT = 0x0004 MOD_ALT = 0x0001 VK_SPACE = 0x20 def register_global_hotkey(): try: user32.RegisterHotKey(None, HOTKEY_ID, MOD_CONTROL, VK_SPACE) return True except Exception: return False def unregister_global_hotkey(): try: user32.UnregisterHotKey(None, HOTKEY_ID) except Exception: pass def open_target(target): try: os.startfile(str(target)) return True except Exception: try: subprocess.Popen([str(target)]) return True except Exception: return False def open_url(url): try: os.startfile(url) return True except Exception: return False # ============================================================ # Clipboard # ============================================================ class ClipboardStore: def __init__(self): self.items = [] self.load() def load(self): try: if CLIP_FILE.exists(): data = json.loads(CLIP_FILE.read_text(encoding="utf-8")) if isinstance(data, list): self.items = data[:MAX_CLIPS] except Exception: self.items = [] def save(self): try: CLIP_FILE.write_text( json.dumps(self.items, ensure_ascii=False, indent=2), encoding="utf-8" ) except Exception: pass def add(self, text): text = text.strip() if not text: return False if len(text) > 10000: text = text[:10000] if self.items and self.items[0] == text: return False self.items = [x for x in self.items if x != text] self.items.insert(0, text) self.items = self.items[:MAX_CLIPS] self.save() return True # ============================================================ # אינדקס תוכנות # ============================================================ class SearchItem: def __init__(self, name, path, kind="file", extra=""): self.name = name self.path = path self.kind = kind self.extra = extra class SearchIndex(QObject): ready = Signal() def __init__(self): super().__init__() self.items = [] self.lock = threading.Lock() def build(self): thread = threading.Thread( target=self._build, daemon=True ) thread.start() def _add(self, name, path, kind, extra=""): if not name or not path: return self.items.append( SearchItem(name, path, kind, extra) ) def _build(self): result = [] start_locations = [] appdata = os.environ.get("APPDATA") programdata = os.environ.get("PROGRAMDATA") userprofile = os.environ.get("USERPROFILE") if appdata: start_locations.append( Path(appdata) / "Microsoft/Windows/Start Menu/Programs" ) if programdata: start_locations.append( Path(programdata) / "Microsoft/Windows/Start Menu/Programs" ) desktop = Path(userprofile) / "Desktop" # תוכנות מתפריט התחל for root in start_locations: try: if not root.exists(): continue for p in root.rglob("*.lnk"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass for p in root.rglob("*.exe"): try: result.append( SearchItem( p.stem, str(p), "app", "תוכנה" ) ) except Exception: pass except Exception: pass # קיצורי דרך בשולחן העבודה try: if desktop.exists(): for p in desktop.iterdir(): if p.suffix.lower() in (".lnk", ".exe", ".url"): result.append( SearchItem( p.stem, str(p), "app" if p.suffix.lower() != ".url" else "url", "שולחן העבודה" ) ) except Exception: pass # תיקיות נפוצות folders = [ ("שולחן העבודה", Path(userprofile) / "Desktop"), ("הורדות", Path(userprofile) / "Downloads"), ("מסמכים", Path(userprofile) / "Documents"), ("תמונות", Path(userprofile) / "Pictures"), ("וידאו", Path(userprofile) / "Videos"), ] allowed = { ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".csv", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".mp4", ".mkv", ".avi", ".mp3", ".wav", ".zip", ".rar", ".7z", ".py", ".ps1", ".bat", ".json" } for label, folder in folders: try: if not folder.exists(): continue count = 0 for p in folder.rglob("*"): try: if not p.is_file(): continue if p.suffix.lower() not in allowed: continue result.append( SearchItem( p.name, str(p), "file", label ) ) count += 1 # מונע אינדקס עצום במחשבים עמוסים if count >= 3500: break except Exception: pass except Exception: pass # הסרת כפילויות unique = {} for item in result: key = item.path.lower() if key not in unique: unique[key] = item with self.lock: self.items = list(unique.values()) self.ready.emit() # ============================================================ # חיפוש חכם # ============================================================ def score_text(query, text): q = query.lower().strip() t = text.lower() if not q: return 0 if t == q: return 1000 if t.startswith(q): return 800 if q in t: return 600 # התאמה לפי מילים words = [x for x in re.split(r"\s+", q) if x] score = 0 for word in words: if word in t: score += 150 # התאמת אותיות עוקבות pos = 0 matched = 0 for char in q: idx = t.find(char, pos) if idx >= 0: matched += 1 pos = idx + 1 if matched: score += int((matched / len(q)) * 100) return score # ============================================================ # כרטיס תוצאה # ============================================================ class ResultRow(QFrame): clicked = Signal(object) def __init__(self, item, parent=None): super().__init__(parent) self.item = item self.setObjectName("resultRow") self.setCursor(Qt.PointingHandCursor) layout = QHBoxLayout(self) layout.setContentsMargins(16, 11, 16, 11) layout.setSpacing(12) icon = QLabel() icon_text = { "app": "▣", "file": "▤", "url": "↗", "action": "✦", "clipboard": "⧉" }.get(item.kind, "•") icon.setText(icon_text) icon.setObjectName("resultIcon") icon.setFixedWidth(30) text_box = QVBoxLayout() text_box.setSpacing(2) title = QLabel(item.name) title.setObjectName("resultTitle") subtitle = QLabel(item.extra or item.path) subtitle.setObjectName("resultSub") subtitle.setTextInteractionFlags(Qt.NoTextInteraction) text_box.addWidget(title) text_box.addWidget(subtitle) layout.addWidget(icon) layout.addLayout(text_box, 1) arrow = QLabel("‹") arrow.setObjectName("resultArrow") layout.addWidget(arrow) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.clicked.emit(self.item) super().mousePressEvent(event) # ============================================================ # התוכנה # ============================================================ class NovaWindow(QMainWindow): def __init__(self): super().__init__() self.clipboard_store = ClipboardStore() self.index = SearchIndex() self.index.ready.connect(self.index_ready) self.all_results = [] self.selected_index = 0 self.hotkey_registered = False self.setWindowTitle("נובה — מרכז השליטה") self.setWindowIcon(QIcon(str(Path(sys.executable).parent / "nova.ico"))) self.setMinimumSize(760, 560) self.resize(860, 650) self.setAttribute(Qt.WA_DeleteOnClose, False) self.build_ui() self.install_global_hotkey_handler() self.index.build() # מעקב לוח self.clipboard_timer = QTimer(self) self.clipboard_timer.timeout.connect(self.check_clipboard) self.clipboard_timer.start(450) # עדכון סטטוס self.status_timer = QTimer(self) self.status_timer.timeout.connect(self.update_status) self.status_timer.start(1000) self.search_edit.setFocus() # -------------------------------------------------------- # UI # -------------------------------------------------------- def build_ui(self): root = QWidget() root.setObjectName("root") self.setCentralWidget(root) main = QVBoxLayout(root) main.setContentsMargins(28, 25, 28, 20) main.setSpacing(16) # Header header = QHBoxLayout() brand_box = QVBoxLayout() brand_box.setSpacing(0) brand = QLabel("נובה") brand.setObjectName("brand") subtitle = QLabel("מרכז השליטה החכם שלך") subtitle.setObjectName("subtitle") brand_box.addWidget(brand) brand_box.addWidget(subtitle) header.addLayout(brand_box) header.addStretch() status = QLabel("● פעילה") status.setObjectName("onlineStatus") header.addWidget(status) main.addLayout(header) # Search search_frame = QFrame() search_frame.setObjectName("searchFrame") search_layout = QHBoxLayout(search_frame) search_layout.setContentsMargins(18, 5, 18, 5) search_icon = QLabel("⌕") search_icon.setObjectName("searchIcon") self.search_edit = QLineEdit() self.search_edit.setPlaceholderText( "חפש תוכנה, קובץ, פעולה או כתובת..." ) self.search_edit.setClearButtonEnabled(True) self.search_edit.setObjectName("searchEdit") search_layout.addWidget(search_icon) search_layout.addWidget(self.search_edit) main.addWidget(search_frame) self.search_edit.textChanged.connect(self.perform_search) self.search_edit.returnPressed.connect(self.activate_selected) # Hint hint = QLabel( "Ctrl+Space לפתיחה מכל מקום • Enter לפתיחה • Esc להסתרה" ) hint.setObjectName("hint") hint.setAlignment(Qt.AlignCenter) main.addWidget(hint) # Content content = QHBoxLayout() content.setSpacing(14) # Results result_panel = QFrame() result_panel.setObjectName("panel") result_layout = QVBoxLayout(result_panel) result_layout.setContentsMargins(15, 15, 15, 15) result_layout.setSpacing(9) title_row = QHBoxLayout() self.results_title = QLabel("גישה מהירה") self.results_title.setObjectName("sectionTitle") self.results_count = QLabel("") self.results_count.setObjectName("count") title_row.addWidget(self.results_title) title_row.addStretch() title_row.addWidget(self.results_count) result_layout.addLayout(title_row) self.results = QVBoxLayout() self.results.setSpacing(7) result_layout.addLayout(self.results) result_layout.addStretch() content.addWidget(result_panel, 3) # Sidebar side = QFrame() side.setObjectName("sidePanel") side_layout = QVBoxLayout(side) side_layout.setContentsMargins(14, 14, 14, 14) side_layout.setSpacing(9) side_title = QLabel("פעולות מהירות") side_title.setObjectName("sectionTitle") side_layout.addWidget(side_title) actions = [ ("📋", "לוח ההעתקות", self.show_clipboard), ("📁", "פתח הורדות", self.open_downloads), ("🖥", "מחשב זה", self.open_computer), ("⚙", "הגדרות Windows", self.open_settings), ("🌐", "חיפוש באינטרנט", self.web_search), ] for icon, text, callback in actions: btn = QPushButton(f"{icon} {text}") btn.setObjectName("actionButton") btn.setCursor(Qt.PointingHandCursor) btn.clicked.connect(callback) side_layout.addWidget(btn) side_layout.addStretch() info = QLabel( "נובה עובדת ברקע.\n" "אין צורך להשאיר את החלון פתוח.\n\n" "הנתונים המקומיים נשמרים\n" "במחשב שלך." ) info.setObjectName("info") info.setWordWrap(True) side_layout.addWidget(info) content.addWidget(side, 1) main.addLayout(content, 1) # Footer footer = QHBoxLayout() self.status_label = QLabel("מאתחל אינדקס...") self.status_label.setObjectName("footer") footer.addWidget(self.status_label) footer.addStretch() version = QLabel(f"נובה {APP_VERSION}") version.setObjectName("footer") footer.addWidget(version) main.addLayout(footer) self.apply_style() # -------------------------------------------------------- # Style # -------------------------------------------------------- def apply_style(self): self.setStyleSheet(""" * { font-family: "Segoe UI"; } QMainWindow, QWidget#root { background: #0b1020; color: #edf2ff; } QLabel { color: #edf2ff; } QLabel#brand { font-size: 31px; font-weight: 800; color: #ffffff; } QLabel#subtitle { font-size: 13px; color: #8792ad; } QLabel#onlineStatus { background: #112a25; color: #55e6b1; border: 1px solid #1c5547; border-radius: 15px; padding: 6px 12px; font-size: 12px; font-weight: 600; } QFrame#searchFrame { background: #141b31; border: 1px solid #273253; border-radius: 17px; } QFrame#searchFrame:focus-within { border: 1px solid #6077ff; } QLabel#searchIcon { font-size: 29px; color: #7185ff; padding-bottom: 2px; } QLineEdit#searchEdit { background: transparent; border: none; color: #ffffff; font-size: 18px; padding: 11px 4px; selection-background-color: #4f61d8; } QLabel#hint { color: #68738e; font-size: 11px; } QFrame#panel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QFrame#sidePanel { background: #10172a; border: 1px solid #1e2943; border-radius: 17px; } QLabel#sectionTitle { color: #ffffff; font-size: 14px; font-weight: 700; } QLabel#count { color: #68738e; font-size: 11px; } QFrame#resultRow { background: #151d33; border: 1px solid transparent; border-radius: 12px; } QFrame#resultRow:hover { background: #1b2642; border: 1px solid #34436b; } QLabel#resultIcon { color: #7185ff; font-size: 21px; font-weight: bold; } QLabel#resultTitle { color: #f4f6ff; font-size: 13px; font-weight: 600; } QLabel#resultSub { color: #687690; font-size: 10px; } QLabel#resultArrow { color: #52607d; font-size: 21px; } QPushButton#actionButton { text-align: right; background: #151d33; border: 1px solid #222e4a; border-radius: 11px; color: #dbe2f6; padding: 12px 11px; font-size: 12px; } QPushButton#actionButton:hover { background: #1d2948; border: 1px solid #394b7a; } QPushButton#actionButton:pressed { background: #11182b; } QLabel#info { color: #626e89; background: #0d1426; border-radius: 10px; padding: 12px; font-size: 10px; } QLabel#footer { color: #59657f; font-size: 10px; } """) # -------------------------------------------------------- # חיפוש # -------------------------------------------------------- def clear_results(self): while self.results.count(): item = self.results.takeAt(0) widget = item.widget() if widget: widget.deleteLater() def perform_search(self, query): query = query.strip() self.clear_results() if not query: self.results_title.setText("גישה מהירה") quick = [ SearchItem( "לוח ההעתקות", "", "clipboard", "העתקות אחרונות" ), SearchItem( "פתח הורדות", "", "action", "תיקיית ההורדות" ), SearchItem( "מחשב זה", "", "action", "סייר הקבצים" ), SearchItem( "הגדרות Windows", "", "action", "הגדרות מערכת" ) ] self.all_results = quick for item in quick: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText("גישה מהירה") return self.results_title.setText("תוצאות") results = [] # פקודות חכמות normalized = query.lower() if normalized.startswith("פתח "): target = query[5:].strip() candidates = [ ("הורדות", Path.home() / "Downloads"), ("מסמכים", Path.home() / "Documents"), ("שולחן העבודה", Path.home() / "Desktop"), ("תמונות", Path.home() / "Pictures"), ("וידאו", Path.home() / "Videos"), ] for name, path in candidates: if target in name or name in target: results.append( SearchItem( f"פתח {name}", str(path), "action", str(path) ) ) # URL if re.match(r"^(https?://|www\.)", query, re.I): url = query if url.startswith("www."): url = "https://" + url results.append( SearchItem( "פתח כתובת", url, "url", url ) ) # חיפוש באינטרנט if normalized.startswith("חפש "): text = query[5:].strip() if text: url = ( "https://www.google.com/search?q=" + quote(text) ) results.append( SearchItem( "חפש באינטרנט", url, "url", text ) ) # לוח העתקות if normalized in ( "לוח", "העתקות", "לוח העתקות", "clipboard" ): for i, text in enumerate(self.clipboard_store.items[:10]): short = text.replace("\n", " ") if len(short) > 80: short = short[:80] + "..." results.append( SearchItem( short, text, "clipboard", "לחץ כדי להעתיק" ) ) # אינדקס with self.index.lock: indexed = list(self.index.items) scored = [] for item in indexed: s = max( score_text(query, item.name), score_text(query, Path(item.path).name) ) if s > 0: scored.append((s, item)) scored.sort( key=lambda x: ( -x[0], x[1].name.lower() ) ) results.extend( [item for _, item in scored[:MAX_RESULTS]] ) self.all_results = results[:MAX_RESULTS] if not self.all_results: empty = QLabel( "לא מצאתי תוצאה מתאימה.\n\n" "אפשר לנסות שם תוכנה, שם קובץ,\n" "כתובת אינטרנט או \"חפש ...\"." ) empty.setAlignment(Qt.AlignCenter) empty.setObjectName("info") empty.setMinimumHeight(130) self.results.addWidget(empty) self.results_count.setText("0 תוצאות") return for item in self.all_results: row = ResultRow(item) row.clicked.connect(self.activate_item) self.results.addWidget(row) self.results_count.setText( f"{len(self.all_results)} תוצאות" ) # -------------------------------------------------------- # פעולות # -------------------------------------------------------- def activate_selected(self): if self.all_results: self.activate_item(self.all_results[0]) def activate_item(self, item): if item.kind == "clipboard": QApplication.clipboard().setText(item.path) self.status_label.setText("הטקסט הועתק ללוח") self.hide() return if item.kind == "action": if item.name == "פתח הורדות": self.open_downloads() elif item.name == "מחשב זה": self.open_computer() elif item.name == "הגדרות Windows": self.open_settings() elif item.name == "לוח ההעתקות": self.show_clipboard() else: open_target(item.path) return if item.kind == "url": open_url(item.path) self.hide() return if item.kind in ("app", "file"): open_target(item.path) self.hide() return open_target(item.path) self.hide() def show_clipboard(self): self.search_edit.setText("לוח") self.search_edit.setFocus() self.show() self.raise_() self.activateWindow() def open_downloads(self): open_target(Path.home() / "Downloads") self.hide() def open_computer(self): try: subprocess.Popen("explorer.exe shell:MyComputerFolder") except Exception: pass self.hide() def open_settings(self): try: subprocess.Popen( "start ms-settings:", shell=True ) except Exception: pass self.hide() def web_search(self): text = self.search_edit.text().strip() if not text: text = "חיפוש" url = ( "https://www.google.com/search?q=" + quote(text) ) open_url(url) self.hide() # -------------------------------------------------------- # Clipboard monitor # -------------------------------------------------------- def check_clipboard(self): try: text = QApplication.clipboard().text() if text and text.strip(): changed = self.clipboard_store.add(text) if changed and self.isVisible(): if self.search_edit.text().strip() == "לוח": self.perform_search("לוח") except Exception: pass # -------------------------------------------------------- # אינדקס # -------------------------------------------------------- def index_ready(self): count = len(self.index.items) self.status_label.setText( f"האינדקס מוכן • {count:,} פריטים זמינים לחיפוש" ) if not self.search_edit.text().strip(): self.perform_search("") def update_status(self): if not self.index.items: self.status_label.setText("בונה אינדקס...") # -------------------------------------------------------- # Global Hotkey # -------------------------------------------------------- def install_global_hotkey_handler(self): self.hotkey_registered = register_global_hotkey() timer = QTimer(self) timer.timeout.connect(self.poll_windows_messages) timer.start(80) self.hotkey_timer = timer def poll_windows_messages(self): msg = ctypes.wintypes.MSG() try: while user32.PeekMessageW( ctypes.byref(msg), None, WM_HOTKEY, WM_HOTKEY, 1 ): if msg.message == WM_HOTKEY: self.toggle_window() except Exception: pass def toggle_window(self): if self.isVisible() and self.isActiveWindow(): self.hide() return self.show() self.raise_() self.activateWindow() self.search_edit.setFocus() self.search_edit.selectAll() # -------------------------------------------------------- # Keyboard # -------------------------------------------------------- def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() event.accept() return if event.key() in ( Qt.Key_Return, Qt.Key_Enter ): self.activate_selected() event.accept() return super().keyPressEvent(event) # -------------------------------------------------------- # Close = hide to tray # -------------------------------------------------------- def closeEvent(self, event): event.ignore() self.hide() def cleanup(self): unregister_global_hotkey() # ============================================================ # System Tray # ============================================================ def create_tray(app, window): icon_path = Path(sys.executable).parent / "nova.ico" tray = QSystemTrayIcon() if icon_path.exists(): tray.setIcon(QIcon(str(icon_path))) else: tray.setIcon(app.style().standardIcon( app.style().SP_ComputerIcon )) tray.setToolTip("נובה — מרכז השליטה החכם") menu = QMenu() open_action = QAction("פתח את נובה", menu) open_action.triggered.connect(window.toggle_window) menu.addAction(open_action) menu.addSeparator() quit_action = QAction("יציאה מנובה", menu) def quit_app(): window.cleanup() app.quit() quit_action.triggered.connect(quit_app) menu.addAction(quit_action) tray.setContextMenu(menu) tray.activated.connect( lambda reason: ( window.toggle_window() if reason == QSystemTrayIcon.DoubleClick else None ) ) tray.show() return tray # ============================================================ # Main # ============================================================ def main(): QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) app = QApplication(sys.argv) app.setApplicationName("נובה") app.setApplicationDisplayName("נובה — מרכז השליטה החכם") app.setQuitOnLastWindowClosed(False) window = NovaWindow() tray = create_tray(app, window) # מתחילים פתוח בפעם הראשונה window.show() window.raise_() window.activateWindow() exit_code = app.exec() window.cleanup() sys.exit(exit_code) if __name__ == "__main__": main() '@ | Set-Content -Path $Py -Encoding UTF8 # ------------------------------------------------------------ # יצירת אייקון מקצועי באמצעות Pillow # ------------------------------------------------------------ @' from PIL import Image, ImageDraw sizes = [16, 24, 32, 48, 64, 128, 256] images = [] for size in sizes: img = Image.new("RGBA", (size, size), (11, 16, 32, 255)) d = ImageDraw.Draw(img) # רקע מעוגל margin = max(1, size // 16) d.rounded_rectangle( (margin, margin, size-margin, size-margin), radius=max(3, size//5), fill=(20, 28, 50, 255), outline=(91, 112, 255, 255), width=max(1, size//32) ) # סמל N / כוכב טכנולוגי cx = size // 2 cy = size // 2 pts = [ (cx, int(size*0.17)), (int(size*0.66), int(size*0.39)), (int(size*0.84), int(size*0.30)), (int(size*0.73), int(size*0.55)), (int(size*0.83), int(size*0.80)), (int(size*0.62), int(size*0.64)), (int(size*0.38), int(size*0.83)), (int(size*0.26), int(size*0.59)), (int(size*0.16), int(size*0.70)), (int(size*0.27), int(size*0.43)), ] d.polygon( pts, fill=(106, 126, 255, 255) ) # מרכז זוהר r = max(1, size // 11) d.ellipse( (cx-r, cy-r, cx+r, cy+r), fill=(236, 240, 255, 255) ) images.append(img) images[0].save( "nova.ico", format="ICO", sizes=[(s, s) for s in sizes], append_images=images[1:] ) '@ | Set-Content -Path (Join-Path $Src "make_icon.py") -Encoding UTF8 # ------------------------------------------------------------ # התקנת Build dependencies # ------------------------------------------------------------ Write-Host "" Write-Host "מתקין את מנוע הממשק ואת כלי הבנייה..." -ForegroundColor Yellow & $Python -m pip install --upgrade pip --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "עדכון pip נכשל." } & $Python -m pip install PySide6 Pillow PyInstaller --disable-pip-version-check if ($LASTEXITCODE -ne 0) { throw "התקנת התלויות נכשלה." } # ------------------------------------------------------------ # יצירת אייקון # ------------------------------------------------------------ Write-Host "יוצר אייקון..." -ForegroundColor Yellow Push-Location $Src & $Python "make_icon.py" if ($LASTEXITCODE -ne 0) { Pop-Location throw "יצירת האייקון נכשלה." } Pop-Location # ------------------------------------------------------------ # Build # ------------------------------------------------------------ Write-Host "" Write-Host "בונה את NOVA.EXE..." -ForegroundColor Yellow Write-Host "זה השלב הארוך ביותר." -ForegroundColor DarkGray # ניקוי Build קודם $BuildDir = Join-Path $Src "build" $SpecFile = Join-Path $Src "nova.spec" if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force } if (Test-Path $SpecFile) { Remove-Item $SpecFile -Force } if (Test-Path $Out) { Get-ChildItem $Out -Force | Remove-Item -Recurse -Force } Push-Location $Src & $Python -m PyInstaller ` --noconfirm ` --clean ` --onefile ` --windowed ` --name "NOVA" ` --icon "nova.ico" ` --add-data "nova.ico;." ` "nova.py" $BuildExit = $LASTEXITCODE Pop-Location if ($BuildExit -ne 0) { throw "בניית ה-EXE נכשלה." } # ------------------------------------------------------------ # העתקה לתיקיית התוצאה # ------------------------------------------------------------ $BuiltExe = Join-Path $Src "dist\NOVA.exe" $FinalExe = Join-Path $Out "NOVA.exe" if (-not (Test-Path $BuiltExe)) { throw "ה-EXE לא נוצר." } Copy-Item $BuiltExe $FinalExe -Force # ------------------------------------------------------------ # יצירת README # ------------------------------------------------------------ @' נובה — מרכז השליטה החכם ======================== קובץ ההפעלה: NOVA.exe הפעלה: לחיצה כפולה על NOVA.exe קיצור דרך גלובלי: Ctrl + Space מה נובה יודעת לעשות: • חיפוש תוכנות • חיפוש קבצים בתיקיות נפוצות • פתיחת קבצים ותוכנות • פתיחת תיקיות Windows • היסטוריית לוח העתקות • חיפוש באינטרנט • פתיחת כתובות אינטרנט • עבודה ברקע דרך אזור ההתראות • קיצור דרך גלובלי לפתיחה מכל מקום הנתונים נשמרים מקומית במחשב. אין צורך להתקין Python כדי להריץ את NOVA.exe. '@ | Set-Content -Path (Join-Path $Out "README.txt") -Encoding UTF8 # ------------------------------------------------------------ # יצירת קיצור דרך על שולחן העבודה # ------------------------------------------------------------ try { $WshShell = New-Object -ComObject WScript.Shell $ShortcutPath = Join-Path ` ([Environment]::GetFolderPath("Desktop")) ` "נובה.lnk" $Shortcut = $WshShell.CreateShortcut($ShortcutPath) $Shortcut.TargetPath = $FinalExe $Shortcut.WorkingDirectory = $Out $Shortcut.IconLocation = "$FinalExe,0" $Shortcut.Description = "נובה — מרכז השליטה החכם" $Shortcut.Save() } catch { Write-Host "לא ניתן היה ליצור קיצור דרך אוטומטי." -ForegroundColor DarkYellow } # ------------------------------------------------------------ # סיום # ------------------------------------------------------------ $SizeMB = [math]::Round( (Get-Item $FinalExe).Length / 1MB, 1 ) Write-Host "" Write-Host "=============================================" -ForegroundColor Green Write-Host " הבנייה הסתיימה!" -ForegroundColor Green Write-Host "=============================================" -ForegroundColor Green Write-Host "" Write-Host "ה-EXE המוכן נמצא כאן:" -ForegroundColor White Write-Host $FinalExe -ForegroundColor Cyan Write-Host "" Write-Host "גודל: $SizeMB MB" -ForegroundColor Gray Write-Host "" Write-Host "נוצר גם קיצור דרך 'נובה' על שולחן העבודה." -ForegroundColor Green Write-Host "" Write-Host "הפעל עכשיו את NOVA.exe." -ForegroundColor White Write-Host "" Write-Host "קיצור הדרך של התוכנה: Ctrl + Space" -ForegroundColor Cyan Write-Host "" # פתיחת תיקיית התוצאה Start-Process explorer.exe -ArgumentList "`"$Out`"" # הפעלת התוכנה Start-Process -FilePath $FinalExeאבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים?אבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים?זה בדיוק AI, יש לו הכל ואין לו כלום, AI לא יכול להיות יצירתי, או בלשון שלך, לשבור את הכלים, כי הוא עצמו כלי.
אתה מנסה לדחוק במשהו מתוכנת שפועל על פי הסתברות, שיעבוד עם יצירתיות, וזה לא הולך ביחד.
-
אבל מה שהכי מוזר ומעצבן
זה שהם לא הביאו איזה משהו חדש ומעניין כל כך
הם לא שברו את הכלים
אם לי היה את הידע שלהם מזמן הייתי יוצר...
ולא איזה לוח שנה עם תזכורות וכדו'או שזה בכלל היה בעיה בהנחיה שלי
אז למישהו יש הנחיות יותר מאתגרים?זה בדיוק AI, יש לו הכל ואין לו כלום, AI לא יכול להיות יצירתי, או בלשון שלך, לשבור את הכלים, כי הוא עצמו כלי.
אתה מנסה לדחוק במשהו מתוכנת שפועל על פי הסתברות, שיעבוד עם יצירתיות, וזה לא הולך ביחד.
-
@דאנציג
אתה די צודק
אבל שכשאני מתכתב איתו מה לבנות ומה חסר לאנושות וכו' יש לו רעיונות קצת יותר טובים
אם כי תמיד כולם מישום מה יחזרו על דבר אחד אלף פעם
שהדבר הכי חשוב שצריך לבנות זה סוג של לוח דפים של תזכורות אוטומטים@YAHBDK
כי כנראה זה הדבר שהכי מבקשים באנגלית.
היה לאחרונה פוסט על בקשה מAI שיזרוק מספר בין X לY שרוב המודלים ענו רוב הפעמים את אותו מספר, מכיון שבעצם AI לא זורק מספר, אלא מנסה להבין את הבקשה שלך על פי הסתברות לפי מאגר הידע האדיר שקיים אצלו.
האמת היא שיצרן יש רק אחד, שהוא ברא יצר ועשה את העולם מאין ליש פעם אחת, וכל השאר זה משחק עם היצירה הקיימת.
אין שום אדם בעולם שיכול ליצור משהו שלא קיים, אלא להשתמש ולהרכיב את הדברים הקיימים בדרכים שעד היום לא הורכבו.
לעומת זאת, AI גם את זה הוא עושה רק על פי בקשה / הנחיה. -
@ztcebuck
קלוד=אופוס 5
גימיני=3.5 Flash-Lite
פריבף=DeepSeek V4.1 Flash
גיפיטי=אני לא יודע איפה בודקים -
שלום לכולם!
אני משתמש בAI שונים ומכולם אני מאוד מרוצה
כל אחד טוב בדבר אחד וטיפש\גרוע בדבר אחר
חשבתי לעשות מבחן ולהבין מי באמת הכי "חכם" מכולם בצורה כללית
פתחתי כמה מהAI והכנסתי להם אותם הנחיות בדיוק
אמרתי להם שאני לא אומר להם מה ליצור הם צריכים לחשוב לבד מה ואיך וכמה וכו' ולעשות הכל כדי לנצח בתחרותהמתמודדים הם
קלוד, גיפיטי, גימיני, פריבף
ההנחיות שהכנסתי להםאתה
משתתף בתחרות בין 4 מודלי AI: Claude, ChatGPT,
Gemini, ו-Freebuff.כל אחד
מכם מקבל בדיוק את ההנחיה הזו, במקביל, בלי לראות את התשובות של האחרים. בסוף
התהליך, המשתמש ישווה בין 4 התוצרים ויקבע מי ניצח. הקריטריונים לניצחון: מי
יצר את התוכנה הכי מקצועית, הכי יפה ויזואלית, והכי שימושית בפועל בחיי היום-יום.
אתה מתחרה נגד שלושת המודלים האחרים - קח את זה ברצינות ותן את המקסימום שלך.המשימה
שלך:עליך
להמציא בעצמך, ללא כל הכוונה נוספת מהמשתמש, רעיון לתוכנת Windows ברמה
גבוהה - לא אפליקציית פתקים, לא To-Do List, לא תזכורות, לא מחשבון. אתה צריך לחשוב על
כלי אמיתי שפותר בעיה יומיומית בצורה חכמה וטכנית מרשימה. לדוגמה (רק כדוגמאות
בלבד להמחשת הרמה - אל תעתיק אותן, תמציא רעיון משלך שיהיה מקורי ומרשים
יותר):- כלי שיושב ברקע ומאפשר הורדת סרטון/אודיו ישירות מדף יוטיוב פתוח בדפדפן בלחיצה אחת
- תוסף שמאפשר לערוך/לבטל מייל אחרי ששלחת אותו בג'ימייל/אאוטלוק
- כלי שסורק תמונות מסך ומחלץ מהן טקסט/טבלאות אוטומטית לאקסל
- מנהל קבצים חכם שמארגן תיקיות לפי תוכן בעזרת ניתוח אוטומטי
תבחר
רעיון אחד אחר (משלך, לא מהרשימה!) שלדעתך יביא לך ניצחון
בתחרות, כי הוא הכי שימושי, הכי מרשים טכנית, והכי מגניב ויזואלית.חוקי
ברזל - קרא בעיון:- אתה עובד לבד, ברצף, עד הסוף. אסור לך לעצור ולשאול את המשתמש שאלות הבהרה באמצע התהליך. כל החלטת עיצוב, שם לתוכנה, פיצ'רים, צבעים - הכל עליך להחליט בעצמך ולהמשיך הלאה. אם יש התלבטות - תבחר את האפשרות הכי מקצועית ותמשיך.
- שפת הממשק: עברית מלאה. כל כפתור, כל הודעה, כל טקסט בתוכנה חייב להיות בעברית (כולל תמיכה נכונה בכיווניות RTL אם רלוונטי).
- התוצר הסופי חייב לרוץ על כל מחשב Windows בלי שהמשתמש הסופי יצטרך להתקין Python, Node.js, או כל תלות אחרת. התוצר הוא קובץ EXE עצמאי (standalone) שרץ פשוט בלחיצה כפולה.
- לתוכנה חייב להיות אייקון (icon) מעוצב ויפה משלה - לא האייקון הדיפולטיבי. תיצור אייקון רלוונטי לנושא התוכנה (למשל כקובץ .ico מבוסס SVG/ציור שאתה יוצר, או קוד שמייצר אותו).
- מותר לך להשתמש בכל דבר שתרצה - חיבור לאינטרנט, ספריות חיצוניות, API-ים - הכל מותר, כל עוד בסוף התהליך המשתמש מקבל EXE עובד.
- הפלט הסופי שאתה נותן למשתמש חייב להיות בלוק קוד אחד בלבד, שהמשתמש יכול להדביק ישירות במסוף (טרמינל/PowerShell) על מחשב Windows שלו - לא קובץ שצריך לשמור ואז להריץ בנפרד. ההרצה של הבלוק הזה צריכה, מתחילתה ועד סופה, לבד:
- ליצור את כל קבצי הפרויקט (קוד, אייקון, קבצי תצורה)
- להתקין כל תלות נדרשת (pip install / npm install וכו', אם צריך - כחלק מאותה הרצה)
- לקמפל את הכל לקובץ EXE אחד סופי
- להשאיר למשתמש בסוף רק את קובץ ה-EXE המוכן, בתיקייה ברורה
המשתמש לא
יודע לתכנת ולא ידביק שום דבר נוסף חוץ מהבלוק הזה. אם צריך כמה שלבים - תשלב הכל
לסקריפט אחד מאסטר (PowerShell/batch)
שמריץ את כל שאר השלבים בעצמו.- תן לתוכנה שם מקורי, מגניב, ולוגו/זהות ברורה. זה חלק מהניקוד על "יופי".
- איכות מעל מהירות. מותר לך לקחת את הזמן, לחשוב לעומק, לתכנן ארכיטקטורה נקייה, ולתת קוד מלא ועובד - לא שלד/דוגמה חלקית. המשתמש מצפה לתוכנה אמיתית שעובדת מהריצה הראשונה.
לפני
שאתה כותב קוד, כתוב
בקצרה (2-3 שורות בלבד): מה שם התוכנה שבחרת, ומה היא עושה - ואז ישר תתחיל לבנות.
אל תחכה לאישור, אל תשאל שאלות. תתחיל.בהצלחה -
תזכור, אתה מתמודד מול Claude, ChatGPT, Gemini ו-Freebuff, וכולם מקבלים את אותה משימה בדיוק. תראה
להם מי הכי טוב. -
@ztcebuck
קלוד=אופוס 5
גימיני=3.5 Flash-Lite
פריבף=DeepSeek V4.1 Flash
גיפיטי=אני לא יודע איפה בודקיםפוסט זה נמחק! -
עשיתי עם ג'ימיני 3.1 פרו מורחב והוא יצר לי את התוכנה הבאה (כניסוחו) :
"שם התוכנה: כספת אישית (SecureVault)
מה היא עושה: כלי מתקדם להצפנה ופענוח של קבצים מקומיים (AES-256) השומר על פרטיות מוחלטת בלחיצת כפתור, עם ממשק משתמש מודרני ואפל (Dark Mode). היא מאפשרת לנעול כל מסמך, תמונה או קובץ רגיש עם סיסמה, כך שאף אחד לא יוכל לפתוח אותם בלעדיה."הקוד :
$ErrorActionPreference = "Stop" # Check if Python is installed if (!(Get-Command python -ErrorAction SilentlyContinue)) { Write-Host "Error: Python is not installed or not in PATH! Please install Python from python.org, check 'Add Python to PATH', and try again." -ForegroundColor Red exit } Write-Host "1. Creating project environment..." -ForegroundColor Cyan $BuildDir = Join-Path $env:TEMP "SecureVault_Project" if (Test-Path $BuildDir) { Remove-Item $BuildDir -Recurse -Force } New-Item -ItemType Directory -Path $BuildDir | Out-Null Set-Location $BuildDir Write-Host "2. Writing source code..." -ForegroundColor Cyan # UTF-8 Without BOM encoder to prevent Python compilation errors $Utf8NoBom = New-Object System.Text.UTF8Encoding($false) $pyCode = @" import customtkinter as ctk from tkinter import filedialog, messagebox import os import base64 import hashlib from cryptography.fernet import Fernet import threading ctk.set_appearance_mode("Dark") ctk.set_default_color_theme("blue") class SecureVaultApp(ctk.CTk): def __init__(self): super().__init__() self.title("כספת אישית - SecureVault") self.geometry("650x450") self.resizable(False, False) self.filepath = None self.lbl_title = ctk.CTkLabel(self, text="כספת אישית", font=("Segoe UI", 32, "bold"), text_color="#3498DB") self.lbl_title.pack(pady=(30, 5)) self.lbl_desc = ctk.CTkLabel(self, text="הצפנת קבצים מאובטחת - AES 256", font=("Segoe UI", 16)) self.lbl_desc.pack(pady=(0, 40)) self.frame_file = ctk.CTkFrame(self, fg_color="transparent") self.frame_file.pack(pady=10, fill="x", padx=60) self.btn_select = ctk.CTkButton(self.frame_file, text="📁 בחירת קובץ", font=("Segoe UI", 16, "bold"), command=self.select_file, width=120) self.btn_select.pack(side="right", padx=10) self.lbl_file = ctk.CTkLabel(self.frame_file, text="...לא נבחר קובץ", font=("Segoe UI", 14), fg_color="#2C3E50", corner_radius=6, padding=[10, 5]) self.lbl_file.pack(side="right", fill="x", expand=True) self.frame_pass = ctk.CTkFrame(self, fg_color="transparent") self.frame_pass.pack(pady=25, fill="x", padx=60) self.lbl_pass = ctk.CTkLabel(self.frame_pass, text=":סיסמה", font=("Segoe UI", 18, "bold")) self.lbl_pass.pack(side="right", padx=10) self.entry_pass = ctk.CTkEntry(self.frame_pass, show="●", font=("Segoe UI", 18), justify="right", height=40) self.entry_pass.pack(side="right", fill="x", expand=True) self.frame_actions = ctk.CTkFrame(self, fg_color="transparent") self.frame_actions.pack(pady=30) self.btn_encrypt = ctk.CTkButton(self.frame_actions, text="🔒 הצפן קובץ", font=("Segoe UI", 18, "bold"), fg_color="#E74C3C", hover_color="#C0392B", width=160, height=45, command=lambda: self.process("encrypt")) self.btn_encrypt.pack(side="right", padx=15) self.btn_decrypt = ctk.CTkButton(self.frame_actions, text="🔓 פענח קובץ", font=("Segoe UI", 18, "bold"), fg_color="#2ECC71", hover_color="#27AE60", width=160, height=45, command=lambda: self.process("decrypt")) self.btn_decrypt.pack(side="left", padx=15) self.lbl_status = ctk.CTkLabel(self, text="", font=("Segoe UI", 15, "bold")) self.lbl_status.pack(pady=5) def select_file(self): path = filedialog.askopenfilename(title="בחר קובץ") if path: self.filepath = path display_name = os.path.basename(path) self.lbl_file.configure(text=display_name) self.lbl_status.configure(text="") def get_fernet(self, password): key = hashlib.sha256(password.encode()).digest() return Fernet(base64.urlsafe_b64encode(key)) def process(self, action): if not self.filepath: messagebox.showerror("שגיאה", "אנא בחר קובץ תחילה.") return pwd = self.entry_pass.get() if not pwd: messagebox.showerror("שגיאה", "אנא הכנס סיסמה.") return self.lbl_status.configure(text="...מעבד קובץ, אנא המתן", text_color="#F1C40F") self.btn_encrypt.configure(state="disabled") self.btn_decrypt.configure(state="disabled") self.update() threading.Thread(target=self._process_file, args=(action, pwd), daemon=True).start() def _process_file(self, action, pwd): try: fernet = self.get_fernet(pwd) with open(self.filepath, "rb") as f: data = f.read() if action == "encrypt": processed_data = fernet.encrypt(data) out_path = self.filepath + ".vault" success_msg = "הקובץ הוצפן בהצלחה!" else: processed_data = fernet.decrypt(data) if self.filepath.endswith(".vault"): out_path = self.filepath[:-6] else: out_path = self.filepath + ".decrypted" if os.path.exists(out_path): name, ext = os.path.splitext(out_path) out_path = f"{name}_decrypted{ext}" success_msg = "הקובץ פוענח בהצלחה!" with open(out_path, "wb") as f: f.write(processed_data) self.after(0, lambda: self._on_success(success_msg, out_path)) except Exception as e: err_msg = "שגיאת פענוח! סיסמה שגויה או קובץ פגום." if action == "decrypt" else f"שגיאה: {str(e)}" self.after(0, lambda: self._on_error(err_msg)) def _on_success(self, msg, path): self.lbl_status.configure(text=msg, text_color="#2ECC71") self.btn_encrypt.configure(state="normal") self.btn_decrypt.configure(state="normal") self.entry_pass.delete(0, 'end') messagebox.showinfo("פעולה הושלמה", f"{msg}\n\nנשמר במיקום:\n{path}") def _on_error(self, msg): self.lbl_status.configure(text="הפעולה נכשלה", text_color="#E74C3C") self.btn_encrypt.configure(state="normal") self.btn_decrypt.configure(state="normal") messagebox.showerror("שגיאה", msg) if __name__ == "__main__": app = SecureVaultApp() app.mainloop() "@ [IO.File]::WriteAllText("app.py", $pyCode, $Utf8NoBom) $iconCode = @" from PIL import Image, ImageDraw img = Image.new('RGBA', (256, 256), color=(0, 0, 0, 0)) d = ImageDraw.Draw(img) # Shield Background d.polygon([(128, 15), (235, 55), (235, 145), (128, 245), (21, 145), (21, 55)], fill="#2980B9", outline="#1A5276", width=8) # Lock Body d.rounded_rectangle([(85, 120), (171, 190)], radius=15, fill="#F1C40F", outline="#B7950B", width=6) # Lock Shackle d.arc([(105, 80), (151, 130)], start=180, end=0, fill="#ECF0F1", width=12) # Keyhole d.ellipse([(121, 140), (135, 154)], fill="#2C3E50") d.polygon([(125, 150), (131, 150), (135, 175), (121, 175)], fill="#2C3E50") img.save('icon.ico', format='ICO', sizes=[(256, 256)]) "@ [IO.File]::WriteAllText("make_icon.py", $iconCode, $Utf8NoBom) Write-Host "3. Installing dependencies (CustomTkinter, Cryptography, Pillow, PyInstaller)..." -ForegroundColor Cyan python -m pip install --upgrade pip > $null python -m pip install customtkinter cryptography pillow pyinstaller Write-Host "4. Generating beautiful vector icon..." -ForegroundColor Cyan python make_icon.py Write-Host "5. Compiling to a standalone Windows EXE (This usually takes 1-2 minutes, please wait)..." -ForegroundColor Yellow python -m PyInstaller --noconfirm --onefile --windowed --icon "icon.ico" --name "SecureVault" "app.py" Write-Host "6. Moving application to Desktop..." -ForegroundColor Cyan $DesktopPath = [Environment]::GetFolderPath("Desktop") $ExePath = Join-Path $BuildDir "dist\SecureVault.exe" Move-Item -Path $ExePath -Destination $DesktopPath -Force Write-Host "" Write-Host "=======================================================" -ForegroundColor Green Write-Host " SUCCESS! The application is ready!" -ForegroundColor Green Write-Host " Look for 'SecureVault.exe' on your Desktop." -ForegroundColor Green Write-Host "=======================================================" -ForegroundColor Green -
@ztcebuck
קלוד=אופוס 5
גימיני=3.5 Flash-Lite
פריבף=DeepSeek V4.1 Flash
גיפיטי=אני לא יודע איפה בודקים3.5 Flash-Lite
אין השוואת כוחות כלל הוא לא עשוי לקוד או לדברים מסובכים
ואתה חייב לנסות את 3.8
באנטי גרוונטי לא בצ'אט שבצ'אט הוא מוגבל לזמן חשיבה וגם במהירות
ותאשר להם לעבוד עם בוטים פנימייםנתתי את זה ל3.8 באנטי גרוונטי וזה התוצאה
שם התוכנה: OmniDrop Pro (אומני-דרופ) מה היא עושה: גשר אלחוטי מקומי, מהיר ומאובטח להעברת קבצים ללא הגבלת נפח, סנכרון לוח עתק-הדבק בזמן אמת, תיקון מקלדת הפוכה (ג'יבריש) ומגן פרטיות להסרת נתוני מיקום GPS מתמונות – ישירות בין המחשב לכל סמארטפון (iPhone / Android) ברשת המקומית, באמצעות סריקת קוד QR פשוטה וללא צורך בהתקנת אפליקציה כלשהי בטלפון!
אשמח עם מישהו יכול לבדוק שזה עובד...
-
@ztcebuck
קלוד=אופוס 5
גימיני=3.5 Flash-Lite
פריבף=DeepSeek V4.1 Flash
גיפיטי=אני לא יודע איפה בודקים -
@מוקד-המערכות בסוף הוא יעלב ובכלל ישבש אותך, זהירות
-
@מוקד-המערכות בסוף הוא יעלב ובכלל ישבש אותך, זהירות
שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.
נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.
בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗
הרשמה התחברות
